Command模式如何将发送方与接收方分离?

Command模式有一个IReceiver接口,只有很少的方法,并且对应每个方法都有具体的Command对象(用execute()方法实现接口ICommand )。

我已经读过客户端知道具体的接收器和具体的命令,通常客户端在具体的命令对象中设置接收器对象。 那为什么说它解耦了发送者和接收者呢?

当客户端已经知道具体接收器时,我觉得这不是松散耦合,并且在这种情况下客户端也可以直接调用接收器对象上的API(方法)。

您可以将Command模式工作流视为如下。

  1. Command为所有命令声明一个接口,提供一个简单的execute()方法,该方法要求Receiver执行命令。

  2. Receiver知道如何执行请求。

  3. Invoker拥有一个命令,可以通过调用execute方法让Command执行请求。

  4. Client创建ConcreteCommands并为命令设置Receiver

  5. ConcreteCommand定义了动作和接收者之间的绑定。

  6. Invoker调用执行时, ConcreteCommand将在Receiver上运行一个或多个操作。

看看示例代码,以更好的方式理解事物。

 public class CommandDemoEx{ public static void main(String args[]){ // On command for TV with same invoker Receiver r = new TV(); Command onCommand = new OnCommand(r); Invoker invoker = new Invoker(onCommand); invoker.execute(); // On command for DVDPlayer with same invoker r = new DVDPlayer(); onCommand = new OnCommand(r); invoker = new Invoker(onCommand); invoker.execute(); } } interface Command { public void execute(); } class Receiver { public void switchOn(){ System.out.println("Switch on from:"+this.getClass().getSimpleName()); } } class OnCommand implements Command{ private Receiver receiver; public OnCommand(Receiver receiver){ this.receiver = receiver; } public void execute(){ receiver.switchOn(); } } class Invoker { public Command command; public Invoker(Command c){ this.command=c; } public void execute(){ this.command.execute(); } } class TV extends Receiver{ public TV(){ } public String toString(){ return this.getClass().getSimpleName(); } } class DVDPlayer extends Receiver{ public DVDPlayer(){ } public String toString(){ return this.getClass().getSimpleName(); } } 

输出:

 java CommandDemoEx Switch on from:TV Switch on from:DVDPlayer 

回答你的问题:

我读过客户端知道具体的接收器和具体的命令,通常客户端在具体的命令对象中设置接收器对象。 然后为什么说它解耦发送者和接收者

要标准化单词,请将“sender”替换为“invoker”。 现在浏览代码。

  1. Invoker simply executes the ConcreteCommand通过传递ConcreteReceiver Invoker simply executes the ConcreteCommand (在本例中为OnCommand)。
  2. ConcreteCommand executes Command通过ConcreteCommand executes Command ,即ConcreteCommand defines binding between Action and Receiver.
  3. 如果您看到工作流,Invoker不会随附加命令而改变,您可以在Invoker的execute()方法中添加业务逻辑,如java.lang.Thread,如下所述。
  4. 通过这种方式, Client (sender) and Receiver are loosely couple through Invoker, which has knowledge of what command to be executed

此链接中的 线程示例

您可以通过实现Runnable对象来创建Thread。

 Thread t = new Thread (new MyRunnable()).start(); 

=>

  Invoker invoker = new Invoker(new ConcreteCommand()); invoker.start() 

并且你在start()中有逻辑来调用在上面的例子中运行()的ConcreteCommand.execute()。

start()方法将在Thread中调用run()方法。 如果直接直接调用run()方法会发生什么? 它不会被视为线程

像这个线程的start()方法一样,你可以在Invoker中添加一些业务逻辑。

 public synchronized void start() { /** * This method is not invoked for the main method thread or "system" * group threads created/set up by the VM. Any new functionality added * to this method in the future may have to also be added to the VM. * * A zero status value corresponds to state "NEW". */ if (threadStatus != 0) throw new IllegalThreadStateException(); group.add(this); start0(); if (stopBeforeStart) { stop0(throwableFromStop); } } private native void start0(); // Native code is not here but this method will call run() method public void run() { if (target != null) { target.run(); } } 

编辑:

在您的上一次查询

这里我们创建命令对象,Receiver对象和Invoker对象。然后在命令对象中传递接收器对象,然后在调用者对象中传递命令对象。 我们为每个接收器做的就像我们在这里为电视和DVDPlayer做的那样。 同样在方法’主’中,TV和DVDPlayer的对象是已知的并且实际上是已创建的。 我们可以简单地做tvObject.switchOn()和dvdPlayer.switchOn()。 Command模式如何帮助

客户无需担心Receiver类的变化。 Invoker直接在具有Receiver对象的ConcreteCommand工作。 Receiver对象将来可能siwtchOn ()更改为switchOnDevice ()。 但客户互动不会改变。

如果你有两个不同的命令,如switchOn ()和switchOff (),你仍然可以使用相同的Invoker

直接来自维基百科 :

命令模式是一种行为设计模式,其中对象用于封装执行操作或稍后触发事件所需的所有信息。

编辑

在重新阅读了Gang of Four关于Command模式的章节之后,我想到了一个更好的场景。 假设您有一个GUI库,它定义了以下内容:

 public interface Command { public void execute(); } public class Button { private Command command; public Button(Command command) { this.command = command; } public void click() { command.execute(); } } 

在这种情况下,Button是命令的接收者,而创建Buttons实际实例的代码是客户端。 当然,在创建按钮时,必须定义Command接口的一些具体实现。 但GUI库不需要知道这些类; 它只需要界面。 这就是GUI代码与代码分离的方式。

松耦合不是Command的主要目标

这是原始设计模式书中 Command模式的类图:

命令类图

正如您所说, Client了解ConcreteCommandReceiver ,因此没有解耦。

为什么说它将发送者和接收者分离

我的书副本并没有说这是Command模式的目标:

将请求封装为对象,从而允许您使用不同的请求,队列或日志请求参数化客户端,并支持可撤销操作。

安德鲁的回答触及了逻辑线程与命令分离的事实。 当您参考设计模式中描述的模式的序列图时,您可以更好地看到InvokerCommand之间的松散耦合:

命令序列图

许多设计模式定义了一个与变体松散耦合的客户端(例如,访问者,策略,观察者,迭代器等)。 松散耦合是可维护性的一个好处,即所谓的变更设计。 命令很特殊,因为受保护免受更改的客户端是Invoker – 它与ConcreteCommmand类分离。 我认为这是你正在寻找的经典脱钩。 添加新命令将需要更改Client ,但不应该破坏只知道Command抽象的Invoker

我一直认为Command模式是唯一的,因为它的主要目标似乎是提供function需求:撤消,重做,日志记录,宏命令操作,事务等。


编辑

关于IReceiver抽象和与Client和具体Receiver类的解耦:这可能只是与Command一起使用的策略模式。 我引用了原书。 存在许多模式的变体(因此,维基百科并不总是很好的模式参考)。