1

Designing the architecture of a personal project, I've come up with the idea of using the combination of these two patterns to solve an architectural issue. In an MVC context, I need to implement various types of actions triggered by the UI. I want to abstract the controller from the execution logic, with the model being responsible for this. The issue is that I have different types of commands, and depending on the type of command, it should be routed through different logical paths of the program. Therefore, I need not only to delegate the action to be performed to the command itself but also to route it.

This is an example of the design I want to implement:

class ICommand {
public:
    virtual ~ICommand() {}
    virtual void execute() = 0;
    virtual void accept(CommandVisitor& visitor) = 0;
};

class CommandVisitor {
public:
    void visit(SaveFileCommand& command);
    void visit(SaveConfiguration& command);

    void visit(SaveFileCommand& command) {
        // logic
    }

    void visit(SaveConfiguration& command) {
       // logic
    }
};

class SaveFileCommand : public ICommand {
public:
    void execute() override {
        // logic
    }
    void accept(CommandVisitor& visitor) override {
        visitor.visit(*this);
    }
};

class SaveConfiguration : public ICommand {
public:
    void execute() override {
        // logic
    }
    void accept(CommandVisitor& visitor) override {
        visitor.visit(*this);
    }
};

My concern is creating a design that's too complex, and I'm not sure if it's the best approach for my problem. What alternatives do I have besides this?

EDIT: Adding more context about the domain

My application consists of simulating one end of a communication.

In an MVC context, I need to communicate the model and the view bidirectionally through the controller. The controller will do nothing but forward the messages it receives from either end, making it possible for both ends (Model and View) not to know each other. The view will generate various types of messages to send to the model for processing, and the model will generate various messages to send to the view for display. The situation is that I need these messages to take different logical paths within the program once they reach the view or the model. Some examples:

  • In view 1, a configuration message is generated and sent to the model to open the TCP port.
  • In view 2, a protocol message is generated and sent to the model to be sent via TCP.
  • In view 3, a protocol message is generated and sent to the model to be sent via UDP.
  • The model receives a message via TCP and it is displayed in view 2.
  • The model receives a message via UDP and it is displayed in view 3.

Therefore, the logical sequence that any message should follow is:

  • It is generated in view/model.
  • It is sent to the controller and gets forwarded.
  • It reaches the view/model and depending on the type, it is directed to one logical path or another.

In other words, I have a series of different "hierarchical" messages that need to be dispatched according to their type.

EDIT 2: Adding more specific code

class ICommand {
public:
    virtual ~ICommand() {}
    virtual std::string execute() = 0;
    virtual void accept(CommandVisitor& visitor) = 0;
};

class IProtocolMessage : public ICommand {
public:
    virtual ~IProtocolMessage() {}
    // Some specific methods
};

class IConfigMessage : public ICommand {
public:
    virtual ~IConfigMessage() {}
    // Some specific methods
};

class ProtocolMessageOne;
class ConfigurationMessageOne;

class CommandVisitor {
    ViewManager* viewManager;
public:
    CommandVisitor(ViewManager* vm) : viewManager(vm) {}

    void visit(ProtocolMessageOne& command);
    void visit(ConfigurationMessageOne& command);
};

class ProtocolMessageOne : public IProtocolMessage {
public:
    std::string execute() override {
        return "Protocol Message One Executed";
    }
    void accept(CommandVisitor& visitor) override {
        visitor.visit(*this);
    }
};

class ConfigurationMessageOne : public IConfigMessage {
public:
    std::string execute() override {
        return "Configuration Message One Executed";
    }
    void accept(CommandVisitor& visitor) override {
        visitor.visit(*this);
    }
};

class ViewManager {
public:
    void sendToViewOne(const std::string& result) {
        std::cout << "View One: " << result << std::endl;
    }

    void sendToViewTwo(const std::string& result) {
        std::cout << "View Two: " << result << std::endl;
    }

    void receiveGeneralCommand(ICommand* command) {
        CommandVisitor visitor(this);
        command->accept(visitor);
    }
};

void CommandVisitor::visit(ProtocolMessageOne& command) {
    viewManager->sendToViewOne(command.execute());
}

void CommandVisitor::visit(ConfigurationMessageOne& command) {
    viewManager->sendToViewTwo(command.execute());
}
5
  • @DocBrown I've just made an edit to add more context about the domain.
    – Gareg
    Commented Mar 21 at 10:51
  • @DocBrown I've made an edit to clarify the roles. In the case of messages that are directed from the Model to the View, a handler in the View will have the role of accepting the command and executing it at the same time. In this way, both routing the message and executing the specific action are achieved.
    – Gareg
    Commented Mar 22 at 7:53
  • Hint: there is no point in stacking one edit below the next edit and the next and the next, this only makes a post harder to read. This site provides full history of each post, anyone can look on the former versions of a post by clicking at the related "edited x hours ago" link below.
    – Doc Brown
    Commented Mar 22 at 9:05
  • ... in your last edit., you made "execute" return a string, formerly it was just a void function. Is this just misnamed, so "execute" should be better named "toString()", implying it has no side effects?
    – Doc Brown
    Commented Mar 22 at 9:09
  • @DocBrown Correct, "execute" conceptually represents the "toString()" method.
    – Gareg
    Commented Mar 22 at 9:16

2 Answers 2

2

I think you got a little bogged down with the two patterns. Maybe you thought throwing just a few patterns at your problem will improve the design, but IMHO that's not the case here, quite the opposite. As it was a topic of many posts on this site in the past, patterns are not building blocks.

For example, you modeled messages as commands, but execute does not contain any execution logic, it is actually a toString() method. Sorry, but this has little to do with the command pattern from the GoF. Or take the visitor pattern, which is designed for "visiting" hierarchical multi-part, and stable object structures - I fail to see where in your example this structure hides.

Maybe it is best to take a step back and think how to solve this differently, with a completely different pattern: an Event Bus. (a.k.a Message Bus).

An Event Bus is a central object in your application where different listeners (here views or the model, maybe multiple models) can subscribe for listening to certain events (=message types), and different senders can send these events to the bus without knowing beforehand who is listening. Listeners can subscribe and unsubscribe at run time. For example, a view can subscribe when it is opened and unsubscribed when it is closed, which can be at any time the application is running. The important thing here is that neither views nor models know each other. All communication is done unidirectional, for making bidirectional calls, a listener has to react to certain events by sending an acknowledge event back to the Event Bus, for which a sender has to subscribe beforehand.

There are several different ways to implement this (for example, synchronously or asynchronously), and several different ways to structure the messages/events, so you surely have to work out the details by yourself. Still I hope you get the idea. The Event Bus can, for example, keep a dictionary of lists of subscribed listeners, indexed by event type, so it can quickly identify which message/event has to be routed to which listener. This approach is well-known and definitely scalable to several hundreds, if not thousand of event types.

1
  • I like this way of trying to solve the problem. Perhaps I had become too fixated on trying to fit that specific combination of patterns to my problem instead of finding a pattern that fits my problem (as it should be). I will review the design and look for more information about what you suggest. Thank you, you've been a great help.
    – Gareg
    Commented Mar 22 at 10:44
1

Considering that in MVC implementations the controller is the orchestrator then the controller after getting a request gets a visitor instance (from a factory of visitors probably) that visits the available commands that call the overloaded visit method that calls the command's execute method to perform the port operations, implying that each command has a port field. The described solution is built around overloading to dispatch the request to the proper command. To extend the implementation with a new command a corresponding visit method has to be added to CommandVisitor otherwise the controller is unable to dispatch the request to it. It seems an open close principle fault.

What alternatives do I have besides this?

That is what are the available alternatives beside using overloading? The plainer alternative, by my experience, is to iterate through the commands and test whether the command supports the request and if so call its execute method. Rephrased that is to replace the use of overloading (the visitor pattern in this case) with plain testing.

1
  • 1
    Your alternative is simple, which is good, but it's not scalable. If I have 100 messages types, I would have to make 100 checks per request, which is unfeasible. I really want a clean solution based on good design, rather than just simplicity.
    – Gareg
    Commented Mar 22 at 8:30

Not the answer you're looking for? Browse other questions tagged or ask your own question.