Search code examples
oopdesign-patternsencapsulationcommand-pattern

Command pattern - why encapsulate in an object?


Command pattern is for encapsulating commands in objects. But why not use function pointers instead? Why do I need to subclass Command for each operation? Instead I can have different functions and call the function pointers.


Solution

  • But why not use function pointers instead.

    Because function pointers can't store arbitrary state. You'll often want the command to be parametrised when you create it. For example:

    struct command {
        virtual ~command() {}
        virtual void do_it() = 0;
    };
    
    struct say_something : command {
        // store a message to print later
        say_something(std::string message) : message(message) {}
    
        // print the stored message
        void do_it() override {std::cout << message << '\n';}
    
        std::string message;
    };
    
    std::unique_ptr<command> say_hello(new say_something("Hello!"));
    
    // later
    
    say_hello->do_it();  // prints stored string
    

    If you were to use a plain function pointer, then you'd need a different function for everything you might want to print.

    Why I need to subclass Command class for each operation?

    Because that's how old-school OOP works; although as mentioned above, you can use the fact that it's an object to parametrise it rather than subclass it.

    Luckily, modern C++ has better facilities:

    typedef std::function<void()> command;
    
    // print a static string
    command say_hello = []{std::cout << "Hello!\n";};
    
    // store a string to print later
    std::string goodbye = "Goodbye!";
    command say_goodbye = [goodbye]{std::cout << goodbye << '\n';};
    
    // later
    
    say_hello();    // prints static string
    say_goodbye();  // prints string stored in the command