4

I have a tree-like structure of objects. Objects have different types but all of them are inherited from the same base class. Now I want to send some kind of command objects to the nodes. Command object has address of the target node and should be passed through root node and intermediate nodes untill it reaches the target node. Target node accepts command and allows it to perform some actions on the node:

void Node::processCommand (Command& cmd) {
    if(cmd.address == this->address)
        cmd.execute(this);
    else
        child[cmd.address]->processCommand(cmd);
}

First question: is it a command pattern, or may be some of it's kind?

Next, consider I have nodes of classes ClassA and ClassB and I want to send some commands to objects of both classes, and some commands only for objects of ClassB, something like that:

class CommandForBoth {
    void execute (BaseClass* obj) {
         obj->doSomething();
    }
}

class CommandForB {
    void execute (ClassB* obj) {
         obj->doSomethingSpecial();
    }
}

Is it possible to implement that without casts and without multiple methods for passing commands of different types, like:

void Node::processCommand (CommandForBoth& cmd);
void Node::processCommand (CommandForClassA& cmd);
void Node::processCommand (CommandForClassB& cmd);
  • 1
    Not really sure about the first part of your question (other than that if it works I see nothing wrong with it). The second part, however, looks like it will be a good fit for some variant of the Visitor pattern. You may also be able to do something with templates to make it work, but I haven't been working in c++ for a while now so struggle to think the right easy to design templates.... – Jules Feb 20 '17 at 14:55
3

Is it a command pattern ?

The command pattern implies that:

  1. All the commands share the same interface. In C++ this is usually done with inheritance from an abstract Command.
  2. A Client creates a concrete command and sets the Receiver.
  3. An Invoker launches the command, which will carry out its actions for the already defined Receiver.

Your approach does not meet these expectations:

  1. CommandForBoth and CommandB do not share the same interface (but maybe you just didn't mention it in your snippet).
  2. Your command doesn't know its receiver: instead of the Client defining it at creation, it's the Invoker who defines the target at execution.
  3. The Invoker is your initial Node. But your Invoker might not launch the command, but forward it to one of it's child.

Or is it a visitor pattern ?

A visitor pattern is designed for executing operations on an ObjectStructure. The structure is responsible to activate the visitor for its relevant element. Each element is responsible to invoke the visitor for their relevant sub-elements.

This looks very close to what you are doing:

  • The initial Node corresponds to the ObjectStructure,
  • Each Node corresponds to an Element
  • Node::ProcessCommand(Command) corresponds to Element::accept(Visitor)
  • Command corresponds to Visitor
  • Command::execute(Node) corresponds to Visitor::visit(Node)

The only unusual thing is that in a visitor pattern, the successive chain of accept() is in general performed by the elements to call the visitor for all the children, whereas here, the accept() only looks at the addressing and executes the visitor if the addressing matches.

Implementation without cast

The visitor pattern has a solution for your casting issue: each Visitor (so for you, each Command) has an override of visit() (so for you: execute()) for each of the different subtypes of Elements (i.e. Nodes) that exist. In your case, all the overrides but one would implement nothing (or will throw an exception).

This is a drawback of the visitor pattern, that you may not want. Alternatively, if the Nodes are polymorphic, you could easily deviate from the "pure" visitor pattern, and use only one execute(BaseClass*):

class CommandForB {
    void execute (BaseClass* obj) {
         ClassB bobj = dynamic_cast<ClassB*>(obj); 
         if (bobj) 
             bobj->doSomethingSpecial();
         else {  // ouch execute was invoked for an Node it shouldn't 
                // either do nothing, or log the unexpected situation, 
                // or throw an exception
         }
    }
}
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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