1. 命令设计模式(Command Pattern)
1.1 基本介绍
- 定义:将一个请求封装成一个对象,从而让用户使用不同的请求把客户端参数化;对请求排队或记录日志,以及支持可撤销的操作。
1.2 主要角色
-
命令(
Command
):声明了一个给所有具体命令类的抽象接口。 -
接收者(
Receiver
):真正执行命令的角色,那些具体的命令ConcreteCommand
引用它,让它完成命令的执行 -
具体命令(
ConcreteCommand
):具体的执行命令,他们需要实现Command接口。注意:该角色创建好时,一个具体命令和一个接收者就已经建立了联系。通过实现execute方法,来调用接收者的相应操作。execute()方法通常叫做执行方法。
-
调用者(
Invoker
):创建一个具体命令(ConcreteCommand
)对象并确定其接收者,负责按照客户端的指令设置并执行命令。像命令的撤销,日志的记录等功能都要在此类中完成. -
客户端(
Client
):创建一个具体命令(ConcreteCommand
)对象并确定其接收者,以及调用具体命令的调用者(Invoker
)。
1.3 使用场景及注意事项
1.3.1 使用场景
-
在某些场合,比如要对行为进行"记录、撤销/重做、事务"等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将"行为请求者"与"行为实现者"解耦?将一组行为抽象为对象,可以实现二者之间的松耦合,这是命令模式的使用场景。
-
命令模式的意图是将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。当需要将各种执行的动作抽象出来,使用时通过不同的参数来决定执行哪个对象。
-
当要对操作过程记录日志,以便后期通过日志将操作过程重新做一遍时。
1.3.2 注意事项
-
Command 接口非常简单,通常只有一个execute方法,如果要支持撤销操作的话,再加一个unexecute方法。
-
每个具体的命令类内部封装了实际执行命令的那个类(Recevier),或者那些类,以及执行需要的数据。
-
每个具体命令类只完成一个请求,有多少个请求就有多少个命令。
-
Invoker类只认识接口Command,其他的都不认识 -----调用者和实现者分离。
-
客户端类负责生成具体命令,并通过Invoker组装执行。
1.4 优缺点
-
优点:
- 命令模式将行为调用者和各种行为分隔开,降低程序的耦合,便于程序扩展。
- 命令模式将行为的具体实现封装起来,客户端无需关心行为的具体实现。
- 命令模式可为多种行为提供统一的调用入口,便于程序对行为的管理和控制。此外,很容易实现序列操作及实现回调系统。你把命令加到一个列表中,迭代执行就可以实现序列操作了。 因为Java不能将函数作为参数,此处我们可以将命令对象当做参数,而这个对象还可执行,所以就实现了回调功能。
-
缺点:
- 使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个命令都需要设计一个具体命令类,因此某些系统可能需要大量具体命令类,这将影响命令模式的使用。
1.5 案例
-
坤坤是一个在美国上高中的篮球热爱者,他想要向NBA的一些球星来学习篮球技术(Command)。因为坤坤是富哥,所以他直接找到了NBA官方(Client),因为坤坤给的实在太多了,所以直接派NBA总裁肖华(Invoker)来负责安排坤坤的学习课程。坤坤的篮球技术课主要分为三个模块,分别是扣篮(ConcreteCommand),三分(ConcreteCommand),运球(ConcreteCommand)。因此肖华给坤坤找了三位老师,分别是詹姆斯(Receiver),库里(Receiver),欧文(Receiver)。
-
具体代码实现如下:
-
命令(
Command
):Commandpublic interface Command { void execute(); }
-
接收者(
Receiver
):James、Curry、Irvingpublic class James { public String dunk(){ return "詹姆斯教你扣篮"; } }
public class Curry { public String shoot(){ return "库里教你投篮"; } }
public class Irving { public String dribble(){ return "欧文教你运球"; } }
-
具体命令(
ConcreteCommand
):DunkCommand、ShootCommand、DribbleCommandpublic class DunkCommand implements Command{ private James james; public DunkCommand(James james) { this.james = james; } @Override public void execute() { System.out.println(james.dunk()); } }
public class ShootCommand implements Command{ private Curry curry; public ShootCommand(Curry curry) { this.curry = curry; } @Override public void execute() { System.out.println(curry.shoot()); } }
public class DribbleCommand implements Command{ private Irving irving; public DribbleCommand(Irving irving) { this.irving = irving; } @Override public void execute() { System.out.println(irving.dribble()); } }
-
调用者(
Receiver
):XiaoHuaInvokerpublic class XiaoHuaInvoker { private List<Command> commandList = new ArrayList<>(); // 添加命令 public void addCommands(Command command) { commandList.add(command); } // 执行命令 public void executeCommand() { for (Command command : commandList) { command.execute(); } } }
-
客户端(Client):NBAClient
public class NBAClient { public static void main(String[] args) { // 1.创建Invoker XiaoHuaInvoker xiaoHuaInvoker = new XiaoHuaInvoker(); // 2.创建ConcreteCommand和Receiver,并建立联系 DunkCommand dunkCommand = new DunkCommand(new James()); ShootCommand shootCommand = new ShootCommand(new Curry()); DribbleCommand dribbleCommand = new DribbleCommand(new Irving()); // 3.Invoker调用ConcreteCommand xiaoHuaInvoker.addCommands(dunkCommand); xiaoHuaInvoker.addCommands(shootCommand); xiaoHuaInvoker.addCommands(dribbleCommand); xiaoHuaInvoker.executeCommand(); } } /* 詹姆斯教你扣篮 库里教你投篮 欧文教你运球 */
-
参考博客: