设计模式-命令模式(Command Pattern)
一、定义
将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销功能。
二、概念解释
理解命令模式要先理解三个角色
- 命令角色: 声明待执行的命令
- 调用者角色:接收命令,执行命令
- 接收者角色:实际干活的角色,是调用者角色执行的命令中真正处理命令的角色
我们将请求封装成抽象的命令角色,当有不同的请求时,就会有对应的具体命令类,这些具体命令类会被调用者接收和执行,当调用者执行命令时,真正干活的是具体命令类里的接收者
对于命令而言,有执行当然也会有撤销执行,所以定义里会有日志等机制,就是用来做类似回滚的操作
三、场景
我接触过的有职业划分的游戏里,法师具有变化技能,可以将目标变大或者变小,盗贼具有隐形技能,可以将目标隐形或者显性,这里我们可以用命令模式实现玩家(调用者角色)接受命令(命令角色),执行命令,并让法师或者盗贼(接收者)进行具体实现
四、实现
1、类图
2、代码实现
首先定义一个抽象命令类 该命令类有一个公共执行方法
public abstract class ChangeCommand {
/**
* 对目标执行命令
* @param target 目标
*/
public abstract void execute(Target target);
}
定义具体的命令类 代表着不同的命令 其中包含接收者角色去做具体的命令实现
// 改变目标的大小命令
public class ChangeSizeCommand extends ChangeCommand{
private Profession profession;
public ChangeSizeCommand(Profession profession) {
this.profession = profession;
}
@Override
public void execute(Target target) {
profession.changeTargetAttribute(target);
}
}
// 改变目标的隐身状态
public class ChangeVisibilityCommand extends ChangeCommand{
private Profession profession;
public ChangeVisibilityCommand(Profession profession) {
this.profession = profession;
}
@Override
public void execute(Target target) {
profession.changeTargetAttribute(target);
}
}
定义接收者角色的抽象 不同的命令类会有不同的接收者角色去实现
public abstract class Profession {
/**
* 改变目标属性
*/
public abstract void changeTargetAttribute(Target target);
}
具体的接收者角色类 真正干活的角色
// 法师改变目标的大小
public class Mage extends Profession{
@Override
public void changeTargetAttribute(Target target) {
System.out.println("原来的状态" + target.getSize().name());
target.setSize(target.getSize() == SizeEnum.HUGE ? SizeEnum.SMALL : SizeEnum.NORMAL);
System.out.println("改变后的状态" + target.getSize().name());
}
}
// 盗贼改变目标的隐身状态
public class Thieves extends Profession{
@Override
public void changeTargetAttribute(Target target) {
System.out.println("变化前属性" + target.getVisibility().name());
target.setVisibility(target.getVisibility() == VisibilityEnum.VISIBLE ? VisibilityEnum.INVISIBLE : VisibilityEnum.VISIBLE);
System.out.println("变化后属性" + target.getVisibility().name());
}
}
调用者角色 负责接收,执行命令
@Data
public class Player {
private ChangeCommand command;
private Target target;
public void setChangeCommand(ChangeCommand command) {
this.command = command;
}
public void setTarget(Target target) {
this.target = target;
}
/**
* 执行命令
*/
public void action() {
this.command.execute(target);
}
}
本例是改变目标的属性 这里定义一个抽象目标
@Data
public abstract class Target {
private SizeEnum size;
private VisibilityEnum visibility;
}
具体的目标
public class Dragon extends Target{
public Dragon() {
setSize(SizeEnum.HUGE);
setVisibility(VisibilityEnum.VISIBLE);
}
}
用到的枚举类
public enum SizeEnum {
/**
* 小
*/
SMALL,
/**
* 正常
*/
NORMAL,
/**
* 巨大
*/
HUGE
}
public enum VisibilityEnum {
/**
* 可见-显形
*/
VISIBLE(),
/**
* 不可见-隐形
*/
INVISIBLE()
}
大脑产生命令 玩家点击鼠标对目标执行命令 法师或者盗贼去改变目标属性
@SpringBootTest
public class CommandTest {
@Test
public void test() {
// 玩家作为调用者
Player novice = new Player();
// 让法师作为接收者执行具体命令
Profession mage = new Mage();
// 缩小命令并指定接收者
ChangeCommand changeSizeCommand = new ChangeSizeCommand(mage);
// 执行缩小目标为龙
novice.setTarget(new Dragon());
// 调用者接受命令 改变龙的大小
novice.setChangeCommand(changeSizeCommand);
// 调用者执行命令
novice.action();
// 让盗贼作为接收者执行具体命令
Profession thieves = new Thieves();
// 隐身命令并指定接收者
ChangeCommand changeVisibilityCommand = new ChangeVisibilityCommand(thieves);
// 调用者接受命令 改变龙的隐身状态
novice.setCommand(changeVisibilityCommand);
// 调用者执行命令
novice.action();
}
}
五、总结
优点:
- 命令容易扩展,当我要扩展其他的命令时,只需要依次增加具体命令角色、接收者角色即可
- 解耦,调用者和接收者解耦,调用者只需要接收和执行命令即可,具体这个命令谁来处理我不需要指定
缺点:当有N个命令时,命令模式就会膨胀起来