概述
基本介绍
1、命令模式(Command Pattern):“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。但在某些场合,比如要对行为进行“记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将一组行为抽象为对象,实现二者之间的松耦合。这就是命令模式,也叫做Action/Transaction
2、在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需要在程序运行时指定具体的请求接收者即可,此时可以使用命令模式来设计。
3、命令模式是的请求发送者与请求接收者消除彼此之间的耦合,让对象之间调用关系更加灵活,实现解耦。
4、在命令模式中,会将一个请求封装为一个对象,以便使用不同参数来表示不同的请求,命令模式支持可撤销的操作。
5、角色有 Invoker 调用者、Receiver被调用者,MyCommand 具体命令实现了 Command接口,持有接收对象。
类图简介
角色说明
Invoker 调用者角色
Command:是命令角色,需要执行的命令都在这里,可以是接口或者抽象类。
Receiver:接收者角色,如何实施和执行一个相关的操作。
ConcreteCommand:将一个接收者对象与一个动作绑定,调用接收者相应的操作,实现execute或者 undo操作。
举例实现
实现遥控器控制电视和电灯开关的功能,类图解析
代码实现
package com.example.pattern.command;
import lombok.Getter;
import lombok.Setter;
import org.springframework.util.CollectionUtils;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Map;
/**
* 命令模式
*/
public interface Command { // 创建命令接口
// 执行操作
void execute();
// 撤销操作
void undo();
}
// 没有任何命令 就是空执行 用户初始化每个按钮,当调用空命令时,对象不进行任何操作
// 省去对象的空判断
class NoCommand implements Command {
@Override
public void execute() {
}
@Override
public void undo() {
}
}
@Setter
@Getter
class LightOnCommand implements Command { // 打开电灯命令
// 聚合LightReceiver 执行命令的具体类
private LightReceiver light;
public LightOnCommand(LightReceiver lightReceiver) {
this.light = lightReceiver;
}
@Override
public void execute() {
// 调用接收者的具体方法
light.on();
}
@Override
public void undo() {
// 调用接收者的具体方法
light.off();
}
}
@Setter
@Getter
class LightOffCommand implements Command { // 关闭电灯命令
// 聚合LightReceiver 执行命令的具体类
private LightReceiver light;
public LightOffCommand(LightReceiver lightReceiver) {
this.light = lightReceiver;
}
@Override
public void execute() {
// 调用接收者的具体方法
light.off();
}
@Override
public void undo() {
// 调用接收者的具体方法
light.on();
}
}
class LightReceiver {
public void on() {
System.out.println(" 电灯打开了 ");
}
public void off() {
System.out.println(" 电灯关闭了 ");
}
}
@Getter
@Setter
class RemoteControl { // 遥控器类
private Map<String, Command> map;
// 开按钮的命令
private Command[] onCommands;
// 关按钮的命令
private Command[] offCommands;
// 执行撤销的命令
private LinkedList<Command> undoCommandList = new LinkedList<>();
// 构造器 完成初始化
public RemoteControl() {
onCommands = new Command[2];
offCommands = new Command[2];
Arrays.stream(onCommands).forEach(item -> item = new NoCommand());
Arrays.stream(offCommands).forEach(item -> item = new NoCommand());
}
// 给按钮设置命令
public void setCommand(int no, Command onCommand, Command offCommand) {
onCommands[no] = onCommand;
offCommands[no] = offCommand;
}
// 执行开启命令
public void onButtonPushed(int no) {
onCommands[no].execute();
undoCommandList.addLast(onCommands[no]);
}
// 执行关闭命令
public void offButtonPushed(int no) {
offCommands[no].execute();
undoCommandList.addLast(offCommands[no]);
}
// 撤销命令
public void undoButtonPushed() {
if (!CollectionUtils.isEmpty(undoCommandList)) {
Command last = undoCommandList.getLast();
last.undo();
undoCommandList.remove(last);
}
}
}
class Client {
public static void main(String[] args) {
// 使用命令设计模式 通过遥控器对电灯的操作
// 创建电灯的对象
LightReceiver lightReceiver = new LightReceiver();
// 创建电灯打开的命令
LightOnCommand lightOnCommand = new LightOnCommand(lightReceiver);
// 创建电灯关闭的命令
LightOffCommand lightOffCommand = new LightOffCommand(lightReceiver);
// 创建一个遥控器
RemoteControl remoteControl = new RemoteControl();
// 设置遥控器的命令
remoteControl.setCommand(0, lightOnCommand, lightOffCommand);
System.out.println("-----------按下电灯开的按钮-------------");
remoteControl.onButtonPushed(0);
System.out.println("-----------按下电灯关闭的按钮-------------");
remoteControl.offButtonPushed(0);
// 创建电视的对象
TVReceiver tvReceiver = new TVReceiver();
// 创建电视打开的命令
TVOnCommand tvOnCommand = new TVOnCommand(tvReceiver);
// 创建电视关闭的命令
TVOffCommand tvOffCommand = new TVOffCommand(tvReceiver);
// 设置遥控器的命令
remoteControl.setCommand(1, tvOnCommand, tvOffCommand);
System.out.println("-----------按下电视开的按钮-------------");
remoteControl.onButtonPushed(1);
System.out.println("-----------按下电视关闭的按钮-------------");
remoteControl.offButtonPushed(1);
System.out.println("-----------按下撤销的按钮-------------");
remoteControl.undoButtonPushed();
remoteControl.undoButtonPushed();
remoteControl.undoButtonPushed();
remoteControl.undoButtonPushed();
remoteControl.undoButtonPushed();
remoteControl.undoButtonPushed();
}
}
// 命令模式扩展比较简单 下面是新增一个 电视的命令
class TVReceiver {
public void on() {
System.out.println(" 电视打开了 ");
}
public void off() {
System.out.println(" 电视关闭了 ");
}
}
@Getter
@Setter
class TVOnCommand implements Command {
// 聚合LightReceiver 执行命令的具体类
private TVReceiver tv;
public TVOnCommand(TVReceiver tvReceiver) {
this.tv = tvReceiver;
}
@Override
public void execute() {
// 调用接收者的具体方法
tv.on();
}
@Override
public void undo() {
// 调用接收者的具体方法
tv.off();
}
}
@Getter
@Setter
class TVOffCommand implements Command {
// 聚合LightReceiver 执行命令的具体类
private TVReceiver tv;
public TVOffCommand(TVReceiver tvReceiver) {
this.tv = tvReceiver;
}
@Override
public void execute() {
// 调用接收者的具体方法
tv.off();
}
@Override
public void undo() {
// 调用接收者的具体方法
tv.on();
}
}
命令模式优缺点分析
1、将发起请求的对象于执行请求的对象解耦。发起请求的对象是调用者,调用者只要调用命令对象的execute()方法就可以让接收者工作,而不必知道具体的接收者对象是谁,是如何实现的,命令对象会负责让接收者执行请求的动作,也就是说“请求发起者”和“请求执行者”之间的解耦是通过命令对象实现的,命令对象起到了纽带桥梁的作用。
2、设计一个命令队列,只要把命令对象放到队列,就可以多线程执行命令。
3、容易实现对请求的撤销和重做。
4、命令模式的缺点:可能导致某些系统有过多的具体命令类,增加了系统的复杂度,这点在使用的时候需要注意。
5、空命令也是一种设计模式,它为我们省去了空判断操作,在上面的实例中,如果没有空命令,每一个按键功能都需要空判断,代码冗余度加大。
6、命令模式的经典应用场景:界面的一个按钮就是一条命令、模拟CMD(DOS)命令、出发-反馈机制。