Command pattern - why encapsulate in an object?
Asked Answered
P

2

8

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.

Pebble answered 8/4, 2014 at 11:8 Comment(0)
R
12

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
Radu answered 8/4, 2014 at 11:16 Comment(2)
Quoting "If you were to use a plain function pointer, then you'd need a different function for everything you might want to print." But you don't have to. You can call function with different parameters, as you create a command with different arguments.Pebble
@Pebble but the Command pattern doesn't let you supply arguments when you invoke the command, only when you create it. So you need an object to store them so they're available when it's invoked.Radu
Q
6

Command pattern is much more than just executing a function. It encapsulates data and logic inside a class and provides an object that could easily be passed as a parameter. Besides executing tasks, it could also fire events, parse and clean up data and much more, and that's where inheritance and template methods come handy, which you won't get using function pointers. Also, implementing undo and redo is very easy using commands.

Queen answered 8/4, 2014 at 11:33 Comment(1)
+1 for inheritance, undo and redo (multiple functions related to the command), and encapsulating data (i.e. state)Pebble

© 2022 - 2024 — McMap. All rights reserved.