命令模式
1.需求
买了一套智能家电(如照明灯,风扇,洗衣机等等),但是不想每一个家电都用一个对应的app去控制,想做到使用一个“遥控器”统一控制所有电器
2.分析需求
- 如果想实现一个app控制其他所有家电,则其他家电都要提供一个统一的接口给app调用,可以尝试把命令和接收全部进行抽象化,附着在“家电”上。
- 同时可以考虑使用命令模式
3.命令模式的基本介绍
- 命令模式(Command Pattern),可以将一个请求封装为一个对象,以便于使用不同参数表达不同的请求。
- 命令模式使得请求发送者和接收者之间消除彼此的耦合,让对象之间的调用关系变得灵活
- 命令模式中存在的角色:
- 抽象命令类(Command):声明执行命令的接口,拥有执行命令的抽象方法
- 具体命令类(Concrete Command),抽象命令的具体化,内部组合receiver
- 实现者(接收者)(Receiver),执行命令功能的相关操作,是具体命令业务的真正实现者
- 调用者(请求者)(invoker),是请求的发送者,它通常拥有很多的命令对象(list or array),通常访问命令对象从而实现某些命令
4.uml
4.“遥控器”的代码实现
uml
package com.liz.GOF23.command.command_controller.command;
//创建命令接口(命令的抽象)
public interface MyCommand {
//执行某个命令
public void execute();
//撤销某个命令
public void undo();
}
//具体命令类
/**
* 没有任何命令,即空执行:用于初始化状态【默认】
* 使用NoCommand 可以省略对空的判断
* */
public class NoCommand implements MyCommand {
//聚合 LightReceiver,从而执行具体的命令
private MyReceiver light;
public NoCommand(){
}
@Override
public void execute() {
//调用接收者的方法
}
@Override
public void undo() {
//调用接收者的方法
}
}
//命令对象(内部聚合了receiver进行具体操作)
//关灯操作
public class LightOffCommand implements MyCommand {
//聚合 LightReceiver,从而执行具体的命令
private MyReceiver light;
public LightOffCommand(MyReceiver light){
this.light = light;
}
@Override
public void execute() {
//调用接收者的方法
light.off();
}
@Override
public void undo() {
//调用接收者的方法
light.on();
}
}
//命令对象(内部聚合了接收者进行具体操作)
//开灯操作
public class LightOnCommand implements MyCommand {
//聚合 LightReceiver,从而执行具体的命令
private MyReceiver light;
public LightOnCommand(MyReceiver light){
this.light = light;
}
@Override
public void execute() {
//调用接收者的方法
light.on();
}
@Override
public void undo() {
//调用接收者的方法
light.off();
}
}
//抽象接收者
//Receiver
public interface MyReceiver {
//开启
void on();
//关闭
void off();
}
//具体的接收者
public class LightReceiver implements MyReceiver {
public void on(){
System.out.println(" 电灯打开了 ");
}
public void off(){
System.out.println(" 电灯关闭了 ");
}
}
/**
* Invoker
* 命令的发送者:内部放置了多个命令
*/
public class RemoteInvoker {
//命令集合1[开启]
MyCommand[] onCommands;
//命令集合2[关闭]
MyCommand[] offCommands;
//命令 [撤销]
MyCommand undoCommand;
//构造器 完成初始化【初始化每一个指令 => 空操作】
public RemoteInvoker() {
onCommands = new MyCommand[5];
offCommands = new MyCommand[5];
//将所有的指令默认初始化
for (int i = 0; i < 5; i++) {
onCommands[i] = new NoCommand();
offCommands[i] = new NoCommand();
}
}
//给按钮设置需要的命令即可
//!!!!给控制器动态设置指令
public void setCommand(int no, MyCommand onCommand, MyCommand offCommand){
onCommands[no] = onCommand;
offCommands[no] = offCommand;
}
//按下开的按钮
public void onButtonWasPushed(int no){
//!!!找到按下的指令,并调用对应的方法
onCommands[no].execute();
//记录这次的操作 用于撤销
undoCommand=onCommands[no];
}
//按下关的按钮
public void offButtonWasPushed(int no){
//!!!找到按下的指令,并调用对应的方法
offCommands[no].execute();
//记录这次的操作 用于撤销
undoCommand=offCommands[no];
}
//按下撤销按钮
public void undoButtonWasPushed(){
undoCommand.undo();
}
}
//测试类
public class Client {
public static void main(String[] args) {
//使用命令设计模式,通过遥控对电灯的操作
//创建电灯接收者
MyReceiver receiver = new LightReceiver();
//创建电灯开闭的命令(MyReceiver 和 command进行绑定)
LightOnCommand onCommand = new LightOnCommand(receiver);
LightOffCommand offCommand = new LightOffCommand(receiver);
//遥控器
RemoteInvoker controller = new RemoteInvoker();
controller.setCommand(0,onCommand,offCommand);
//遥控器调用命令
System.out.println("-------------按下开启按钮----------------");
controller.onButtonWasPushed(0);
System.out.println("--------------撤销操作------------------");
controller.undoButtonWasPushed();
System.out.println("--------------按下关闭按钮-----------------");
controller.offButtonWasPushed(0);
System.out.println("--------------撤销操作------------------");
controller.undoButtonWasPushed();
}
}
执行结果:
命令模式的细节
-
将发起请求和执行请求的对象进行解耦,发起请求的对象是调用者,调用者(invoker)只要调用命令对象的方法就可以让接收者进行工作,但是并不需要知道具体的接收者(LightOnReceiver)是谁,命令对象(xxCommand)负责让接收者进行执行请求的动作。
-
因此命令对象实现了纽带的作用,将发送者和接收者进行联系
-
命令对象的优缺点:
- 优点: 实现低耦合,新的命令可以很容易添加到系统中去
- 缺点: 可能导致某些系统有过多的命令类,增加系统的复杂度
-
命令模式的使用环境:
- 设置界面按钮和指令,模拟CMS (DOS)命令订单的撤销和恢复,实现触发反馈机制
参考资料: 韩顺平java设计模式