Command模式如何将发送方与接收方分离?
Command模式有一个IReceiver接口,只有很少的方法,并且对应每个方法都有具体的Command对象(用execute()方法实现接口ICommand
)。
我已经读过客户端知道具体的接收器和具体的命令,通常客户端在具体的命令对象中设置接收器对象。 那为什么说它解耦了发送者和接收者呢?
当客户端已经知道具体接收器时,我觉得这不是松散耦合,并且在这种情况下客户端也可以直接调用接收器对象上的API(方法)。
您可以将Command模式工作流视为如下。
-
Command
为所有命令声明一个接口,提供一个简单的execute()方法,该方法要求Receiver执行命令。 -
Receiver
知道如何执行请求。 -
Invoker
拥有一个命令,可以通过调用execute方法让Command
执行请求。 -
Client
创建ConcreteCommands
并为命令设置Receiver
。 -
ConcreteCommand
定义了动作和接收者之间的绑定。 -
当
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”。 现在浏览代码。
-
Invoker simply executes the ConcreteCommand
通过传递ConcreteReceiverInvoker simply executes the ConcreteCommand
(在本例中为OnCommand)。 -
ConcreteCommand executes Command
通过ConcreteCommand executes Command
,即ConcreteCommand defines binding between Action and Receiver.
- 如果您看到工作流,Invoker不会随附加命令而改变,您可以在Invoker的
execute()
方法中添加业务逻辑,如java.lang.Thread,如下所述。 - 通过这种方式,
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
了解ConcreteCommand
和Receiver
,因此没有解耦。
为什么说它将发送者和接收者分离
我的书副本并没有说这是Command模式的目标:
将请求封装为对象,从而允许您使用不同的请求,队列或日志请求参数化客户端,并支持可撤销操作。
安德鲁的回答触及了逻辑线程与命令分离的事实。 当您参考设计模式中描述的模式的序列图时,您可以更好地看到Invoker
和Command
之间的松散耦合:
许多设计模式定义了一个与变体松散耦合的客户端(例如,访问者,策略,观察者,迭代器等)。 松散耦合是可维护性的一个好处,即所谓的变更设计。 命令很特殊,因为受保护免受更改的客户端是Invoker
– 它与ConcreteCommmand
类分离。 我认为这是你正在寻找的经典脱钩。 添加新命令将需要更改Client
,但不应该破坏只知道Command
抽象的Invoker
。
我一直认为Command模式是唯一的,因为它的主要目标似乎是提供function需求:撤消,重做,日志记录,宏命令操作,事务等。
编辑
关于IReceiver
抽象和与Client
和具体Receiver
类的解耦:这可能只是与Command一起使用的策略模式。 我引用了原书。 存在许多模式的变体(因此,维基百科并不总是很好的模式参考)。