Search code examples
c++design-patternsshared-ptrcommand-pattern

Working on a solid Command Pattern with shared_ptr


I am trying to implement a very clean Command Pattern in a library.

I have the following structure right now (a few parts are still being finished up):

  1. users (client-code) have some Object, call it "Manager"
  2. Manager holds a collection of shared_ptr<Foo>
  3. Manager provides access to the collection by returning shared_ptr<Foo>
  4. I have a Command abstract class and a hierarchy of commands for actions to perform on Foo
  5. Client code should not call Command::execute(), only Manager should, Manager::execute(shared_ptr<Command>), so that it can handle undo/redo

I would like to follow the following rules:

  1. users (client-code) have some Object, call it "Manager"
  2. Manager holds a collection of shared_ptr<Foo>
  3. Manager provides access to the collection by returning shared_ptr<const Foo>
  4. I have a Command abstract class and a hierarchy of commands for actions to perform on Foo
  5. Client code cannot (without workarounds) call Command::execute(), only Manager can, Manager::execute(shared_ptr<Command>), so that it can handle undo/redo and get non-const smart pointers
  6. A Manager must be able to allow Command objects to access and modify shared_ptr<Foo> even though the user initializes Command objecst with shared_ptr<const Foo>

I am just trying to figure out the best way to handle giving out shared_ptr<const Foo> while allowing number 5 and 6 to work.

Is there any example/design pattern that does this which I could learn from? Is this a good idea compared to what I already have/am working on?


Solution

  • Since it wouldn't make any sense to me otherwise, I'm going to assume that

    • your library provides the Manager class (or at least a base class), and
    • clients have to use that class to invoke a Command.

    In that case, maybe something like this could work:

    void Manager::execute(Command& cmd, shared_ptr<Foo const> const& target)
    {
        shared_ptr<Foo> mutableTarget = this->LookupMutableFoo(mutableTarget); // throws if not found
        cmd.execute(mutableTarget); // client cannot invoke without mutable pointer
    }
    
    // or, if the "target" needs to be stored in the Command you could use something like this:
    void Manager::execute(Command& cmd)
    {
        shared_ptr<Foo> mutableTarget = this->LookupMutableFoo(cmd.GetTarget()); // throws if not found
        cmd.execute(mutableTarget); // client cannot invoke without mutable pointer
    }
    

    I'm not sure though if using const is the best solution here. Maybe you should wrap your Foo objects in e.g. ClientFoo objects. The manager only hands out pointers to ClientFoo. The manager can then (e.g. via friend) get the Foo from the ClientFoo, and use it to invoke the Command.