Command Pattern seems needlessly complex (what am

2019-01-26 06:12发布

问题:

I've read up on the Command Pattern, and I think I'm missing something. The Command object exists to abstract away the details of the Receiver object. It seems to me that we could simply stop here, and hold references to Command objects to execute the appropriate method at the appropriate time.

Why, then, is the Invoker needed? What advantage does this additional indirection provide? We've already hidden the details of the Receiver behind the Command, what's the motivation for the Command to then be hidden from the client as well?

回答1:

Well, if you put it that way, it seems quite complex, but often a Receiver doesn't need to be an object at all. It can be little more than just a function that is executed (as an event). Also, the invoker doesn't need to be a class. It is just the thing that triggers the command. This also can be an event handler in a button.

Even Wikipedia sums up a couple of examples where this pattern is used without actually having to implement complete separate classes for invoker and receiver. An example is a wizard dialog, where the GUI populates the command object, and a Finish button triggers it. So that GUI class (that you have anyway) is both the client and the invoker.



回答2:

If you are passing different type of commands, Invoker is useful. You can use same Invoker for execution of different concrete commands. On a different node, tagging Receiver with ConcreteCommand instead of Invoker allows loose coupling. The Receiver may change the name of the method (e.g. switchOn to swithcOnTV) as in this example:

Related post: Using Command Design pattern

To understand the purpose of Invoker,I would like you to refer this article on Restaurant & Car Service centre use cases.

The waiter (Invoker) takes the order from the Customer on his pad. The Order is then queued for the order cook and gets to the cook (Receiver) where it is processed.

The Client is the Customer. He sends his request to the Receiver through the waiter, who is the Invoker. The waiter encapsulates the command (the order in this case) by writing it on the check and then places it, creating the ConcreteCommand object which is the command itself.

The Receiver will be the cook that, after completing work on all the orders that were sent to him before the command in question, starts work on it.

Another noticeable aspect of the example is the fact that the pad for the orders does not support only orders from the menu, so it can support commands to cook many different items.



回答3:

From what I can tell, the whole point of the pattern is to have some sort of command producer and some sort of command consumer, but allow the producer to create or modify commands without the consumer changing.

The pattern calls the producer the "Client" and the consumer the "Invoker".

It is an OO callback.

Why, then, is the Invoker needed

As far as I can tell from all the examples on Wikipedia, the invoker doesn't have a definite form. It is simply some code that accepts an abstract command.

It seems to me that we could simply stop here, and hold references to Command objects

If it makes sense in your code for the thing that invokes commands to accept or hold references to abstract commands, then you've already implemented the invoker.

If one bit of code is both the producer and the consumer, the command pattern is worthless. It is only worthwhile when you are passing abstract commands to something that wants to invoke them.



回答4:

We've already hidden the details of the Receiver behind the Command,

That's exactly right, but who is hiding those details and who are they hidden from? The answer is that whomever instantiates the Command implementation is doing the hiding, and whomever invokes the Command abstraction is hidden from. Clearly it makes no sense for both of those actions to be performed by one object, any more than you can hide something from yourself.

Thus, the Client instantiates a ConcreteCommand and passes it to the Invoker, who is only aware of the Command interface. In effect, the Client performs dependency injection for the Invoker.

Also note there are different ways to implement a ConcreteCommand (see https://stackoverflow.com/a/35617012/1371329). If the ConcreteCommand has some mechanism to dynamically discover its own Receiver, then dependency injection may be unnecessary.