Command模式有一个IReceiver接口,只有很少的方法,并且对应每个方法都有具体的Command对象(用execute()方法实现接口ICommand
)。
我已经读过客户端知道具体的接收器和具体的命令,通常客户端在具体的命令对象中设置接收器对象。那为什么说它解耦发送者和接收者?
当客户端已经知道具体的接收器时,我觉得这不是松耦合,在这种情况下客户端也可以直接调用接收器对象上的API(方法)。
答案 0 :(得分:2)
直接来自Wikipedia:
命令模式是一种行为设计模式,其中一个对象用于封装执行操作或稍后触发事件所需的所有信息。
修改强>
重新阅读命令模式的Gang of Four部分后,我想到了一个更好的方案。假设您有一个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代码与代码分离的方式。
答案 1 :(得分:2)
您可以将Command模式工作流视为如下。
Command
为所有命令声明一个接口,提供一个简单的execute()方法,该方法要求Receiver执行该操作。
Receiver
知道如何执行请求。
Invoker
拥有一个命令,可以通过调用execute方法让Command
执行请求。
Client
创建ConcreteCommands
并为该命令设置Receiver
。
ConcreteCommand
定义了动作和接收者之间的绑定。
执行Invoker
次来电时,ConcreteCommand
会在接收方上执行一项或多项操作。
查看示例代码,以更好的方式理解事物。
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 simply executes the ConcreteCommand
(在本例中为OnCommand)。 ConcreteCommand executes Command
,即ConcreteCommand defines binding between Action and Receiver.
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 Object。然后在命令对象中传递receiver对象,然后在invoker对象中传递命令对象。我们为每个接收器做的就像我们在这里为电视和DVDPlayer做的那样。同样在方法&#39; main&#39; TV和DVDPlayer的对象是已知的并且实际上是已创建的。我们可以简单地做tvObject.switchOn()和dvdPlayer.switchOn()。 Command模式如何帮助
客户无需担心Receiver
课程的变化。 Invoker
直接在ConcreteCommand
上运行,Receiver
具有Receiver
个对象。 siwtchOn
对象将来可能会将switchOnDevice
()更改为switchOn
()。但客户互动不会改变。
如果您有两个不同的命令,例如switchOff
()和Invoker
(),您仍然可以使用相同的<div ng-app="myApp" ng-controller="myCtrl">
<select class="form-control input-sm" id="accountSelect"
ng-model="option.account"
ng-options="account.id as account.name for account in accounts">
</select>
</div>
。
答案 2 :(得分:1)
这里是原始Design Patterns book的命令模式的类图:
正如您所说,Client
知道ConcreteCommand
和Receiver
,所以那里没有脱钩。
为什么说它将发送者和接收者分离
我的书副本并没有说这是Command模式的目标:
将请求封装为对象,从而允许您使用不同的请求,队列或日志请求参数化客户端,并支持可撤销的操作。
安德鲁的答案触及逻辑线程与命令分离的事实。当您参考设计模式中描述的模式的序列图时,您可以更好地看到Invoker
和Command
之间的松散耦合:
许多设计模式定义了与变体松散耦合的客户端(例如,访问者,策略,观察者,迭代器等)。松耦合是可维护性的一个好处,即所谓的变更设计。命令很特殊,因为受到更改保护的客户端是Invoker
- 它与ConcreteCommmand
类分离。我认为这是你正在寻找的经典脱钩。添加新命令需要更改Client
,但不应该中断Invoker
,只知道Command
抽象。
我一直认为Command模式是唯一的,因为它的主要目标似乎是提供功能需求:撤消,重做,日志记录,宏命令操作,事务等。
关于IReceiver
抽象和与Client
和具体Receiver
类的解耦:这可能只是与Command一起使用的策略模式。我引用了原书。存在许多模式的变体(因此,维基百科并不总是模式的很好的参考)。