顿搜
命令模式——23种设计模式之行为型模式
耦合是软件不能抵御变化灾难的根本性原因。 不仅实体对象与实体对象之间存在耦合关系, 实体对象与行为操作之间也存在耦合关系。在软件系统中,“行为请求者”与“行为实现 者”通常呈现一种“紧耦合”。但在某些场合, 比如要对行为进行“记录、撤销/重做、事务” 等处理,这种无法抵御变化的紧耦合是不合适 的。
在这种情况下,如何将“行为请求者”与“行 为实现者”解耦?Command模式将一组行为抽象为对象,可以实现二者 之间的松耦合。
解决的问题:
命令模式主要解决控制器发出命令,执行者执行命令相关的一类问题。
传统方法问题所在:
当控制器需要给某台设备下命令执行某项操作时,如果直接调用该设备的该命令,这种关系是强耦合的。当某台设备出现故障,需要更新设备时,就要修改控制器程序,调用新的设备来执行该命令。或者设备的命令更改了,也需要修改控制器程序,调用该设备的新命令。产生这种问题的根源在于控制器、设备、命令是一一对应的。
模式思想:
命令模式是将控制、命令、执行三者分离开,将他们分别封装成对象,从而达到命令的发出者和执行者之间解耦,实现控制、命令、执行分开,以便于代码扩展。以下是命令模式的一个例子。
一、类图表示
命令模式涉及到Command(抽象命令类),ConcreteCommand(具体命令类),Client(客户类),Invoker(调用者),Receiver(接收者)等角色
- ①、Command(抽象命令类):声明执行操作的接口
- ②、ConcreteCommand:将一个接收者对象绑定于一个动作,调用接收者相应的操作以实现Execute
- ③、Client(客户类):创建一个具体命令对象并设定它的接收者
- ④、Invoker(调用者):要求该命令执行这个请求
- ⑤、Receiver(接收者):知道如何实施与执行一个请求相关的操作。任何类都可能作为一个接收者
二、代码示例
//命令接口
public interface Command{
public void execute();
}//A命令对应的操作,具体谁执行A命令,由传入的执行者receiver来决定
public class Command_A implements Command{
private Receiver receiver;
public Command_A(Receiver receiver){
this.receiver =receiver;
}
@override
public void execute(){
receiver.do_A();
}
}//B命令对应的操作,具体谁执行B命令,由传入的执行者receiver来决定
public class Command_B implements Command{
private Receiver receiver;
public Command_B(Receiver receiver){
this.receiver =receiver;
}
@override
public void execute(){
receiver.do_B();
}
}//执行者1
public class Receiver_1{
public void do_A(){
System.out.println("I am Receiver_1 do_A");
}
public void do_B(){
System.out.println("I am Receiver_1 do_B");
}//执行者2
public class Receiver_2{
public void do_A(){
System.out.println("I am Receiver_2 do_A");
}
public void do_B(){
System.out.println("I am Receiver_2 do_B");
}//控制器接口
public interface Controler{
public void command_L();
public void command_R();
}//控制器实现类
public class ControlerImpl implements Controler{
private Command[] commands_L;
private Command[] commands_R;
public ControlerImpl(){
commands_L = new Command[2];
commands_R = new Command[2];
}
public void setCommand(int index,Commoand command_L,Command command_R){
commands_L[index] = command_L;
commands_R[index] = command_R;
}
@override
public void command_L(int index){
commands_L[index].execute();
}
@override
public void commands_R(int index){
commands_R[index].extcute();
}
}//测试类
public class Test{
public static void main(){
Receiver_1 receiver_1 = new Receiver_1();
Receover_2 receiver_2 = new Receiver_2();
Command command_A1 = new Command_A(receiver_1);
Command command_A2 = new Command_A(receiver_2);
Command command_B1 = new Command_B(receiver_1);
Command command_B2 = new Command_B(reveiver_2);
Controler controler = new ControlerImpl();
//0号位置左边由receiver_1执行A命令,右边由receiver_1执行B命令
controler.setCommand(0,command_A1,command_B1);
//1号位置左边由receive_2执行A命令,右边由receiver_2执行B命令
controler.setCommand(1,command_A2,command_B2);
controler.commands_L(0);//0号位置执行左边按钮
controler.commands_R(0);//0号位置执行右边按钮
controler.commands_L(1);//1号位置执行左边按钮
controler.commands_R(1);//1号位置执行右边按钮
}
}三、模式分析
命令模式的本质是对命令进行封装,将发出命令的责任和执行命令的责任分割开。
- ①、每一个命令都是一个操作:请求的一方发出请求, 要求执行一个操作;接收的一方收到请求,并执行操作。
- ②、命令模式允许请求的一方和接收的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,,更不必知道请求是怎么被接收,以及操作是否被执行、何时被执行,以及是怎么被执行的。
四、优缺点分析
1、优点
- ①、将调用操作的对象与知道如何实现该操作的对象解耦
- ②、Command是first class object。它们可像其它对象一样被操纵和扩展
- ③、增加新的Command很容易,无需改变已有的类
- ④、将多个Command装配成一个复合Command,一般说来,复合Command是Composite模式的一个实例
2、缺点
- ①、会导致某些系统有过多的具体命令类
五、模式间关系
1、与组合模式的关系
- ①、通过使用组合模式,可以将多个“命令”封装为一 个“复合命令”。
2、与调停者模式的关系
- ①、结合调停者模式来实现undo/redo
3、与原型模式的关系
- ①、Command被放到 history list之前,可以用Prototype模式复制自身
六、总结
在众多设计模式中,Command模式是很简单也 很优雅的一种设计模式。
- ①、命令模式将控制器和命令分离开,将命令独立出来,抽象出命令接口,然后将各种命令实现成相应的一个个对象,控制器并不需要知道与哪个命令相对应,只需调用命令接口即可,而在具体实现时,再将控制器按钮与具体命令进行绑定。这样当命令发生变化时,只需重新进行绑定,无需更改控制器按钮。
- ②、命令模式将命令和执行者分离开,命令只关心执行的是那种命令,而具体由谁来执行,则可以通过外部传入相应的执行者设备,所以当设备更新后,只需给命令传入新的设备即可,在面向对象语言中,常见的实现手段是将“行为”抽象为“对象”。
- ③、命令模式的关键在于引入了抽象命令接口,发送者针对抽象命令接口编程,只有实现了抽象命令接口的具体命令才能与接收者相关联。
- ④、抽象出待执行的动作以参数化某对象,可以代 替“回调”函数,在不同的时刻指定、排列和执行请求。支持取消操作。Execute操作可以在实施前将状态存储起来,支持修改日志,这样当系统崩溃时,这些修改 可以被重做一遍。
