命令模式
定义
命令模式(Command Pattern
):将一个请求封装为一个对象,从而让我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。命令模式是一种对象行为型模式,其别名为动作(Action)模式或事务(Transaction)模式。
场景
开发一个Android App,定义了很多手势操作,比如单指左滑右滑、单击双击、双指上滑下滑等,这些手势操作会在多种组件上生效,比如图片、文本、容器等,并且以后可能新增更多的命令,并且一个手势操作可能同时在多个组件上生效。
UML类图
代码
command
示例:
public class TestCommand {
@Test
public void test() {
new TextView(Collections.singletonList(new LongPress(new ContextMenuHandler()))).apply();
System.out.println();
new ImageView(Collections.singletonList(new LongPress(new PhotoGalleryHandler()))).apply();
}
/*
Command from TextView...
Invoke gesture LongPress...
Handle LongPress...
Display context menu...
Command from ImageView...
Invoke gesture LongPress...
Handle LongPress...
Display photo gallery...
*/
}
总结
咋一看起来好像是多个维度的变化,直接使用桥接模式即可,至于可能出现需要同时在多个组件上生效的场景,完全可以将桥接实现部分的引用变成一个集合。但区别在于桥接模式实现类的行为却不能再扩展,客户端可以自由组合两个维度,但维度一旦组合就意味着行为已经确定。命令模式对其中一个维度的行为继续抽象,可以理解为这个维度又是一个桥接模式,客户依然可以随意连接两个维度,但是其中一个维度的行为却可以另外指定,相当于是三个维度的桥接模式,桥接模式注重维度关系,命令模式注重行为:调用者、命令、接受者这三个维度可以自由变化,两种模式的使用场景是不同的。
上面的实现方式是和组合模式结合起来的,一个命令可以是由多个命令组和起来的,这样一组命令也可以叫做命令队列或宏命令。
命令模式是一种使用频率非常高的设计模式,它可以将请求发送者与接收者解耦,请求发送者通过命令对象来间接引用请求接收者,使得系统具有更好的灵活性和可扩展性。在基于GUI的软件开发,无论是在电脑桌面应用还是在移动应用中,命令模式都得到了广泛的应用。
主要优点:
- 降低系统的耦合度。由于请求者与接收者之间不存在直接引用,因此请求者与接收者之间实现完全解耦,相同的请求者可以对应不同的接收者,同样,相同的接收者也可以供不同的请求者使用,两者之间具有良好的独立性。
- 新的命令可以很容易地加入到系统中。由于增加新的具体命令类不会影响到其他类,因此增加新的具体命令类很容易,无须修改原有系统源代码,甚至客户类代码,满足“开闭原则”的要求。
- 可以比较容易地设计一个命令队列或宏命令(组合命令)。
- 为请求的撤销(Undo)和恢复(Redo)操作提供了一种设计和实现方案。
主要缺点:
- 使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个对请求接收者的调用操作都需要设计一个具体命令类,因此在某些系统中可能需要提供大量的具体命令类,这将影响命令模式的使用。
适用场景:
- 系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。请求调用者无须知道接收者的存在,也无须知道接收者是谁,接收者也无须关心何时被调用。
- 系统需要在不同的时间指定请求、将请求排队和执行请求。一个命令对象和请求的初始调用者可以有不同的生命期,换言之,最初的请求发出者可能已经不在了,而命令对象本身仍然是活动的,可以通过该命令对象去调用请求接收者,而无须关心请求调用者的存在性,可以通过请求日志文件等机制来具体实现。
- 命令执行过程可以被记录,当系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作时,可以从这些记录中恢复。
- 系统需要将一组操作组合在一起形成宏命令。