设计模式-命令模式
命令(Command)模式属于对象的行为模式。命令模式又称为行动(Action)模式或交易(Transaction)模式。(学习)
命令模式把一个请求或者操作封装到一个对象中。命令模式允许系统使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。
命令模式的结构
命令模式是对命令的封装。命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象。
每一个命令以都是一个操作:请求的一方发出请求要求执行一个操作;接收的一方收到请求,并执行操作。命令模式允许请求的一方和接收的一方独立开来,便得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否被执行、何时被执行,以及是怎么被执行的。
命令允许主求的一方和接收请求的一方能够独立演化,从而具有以下的优点:
- 命令模式使新的命令很容易地被加入到系统里;
- 允许接收请求的一方决定是否要否决请求;
- 能较容易地设计一个命令队列;
- 可以容易地实现对请求的撤销和恢复;
- 在需要的情况下,可以较容易地将命令记入日志;
下面以一个示意性的系统,说明命令模式的结构。
命令模式涉及到五个角色,它们分别是:
- 客户端角色:创建一个具体命令(ConcreteCommand)对象并确定其接收者。
- 命令角色:声明了一个给所有具体命令类的抽象接口。
- 具体命令角色:定义一个接收者和行为之间的弱耦合;实现execute()方法,负责调用接收者的相应操作。execute()方法通常叫做执行方法。
- 请求者角色:负责调用命令对象执行请求,相关的方法叫做行动方法。
- 接收者角色:负责具体实施和执行一个请求。任命一个类都可以成为接收者,实施和执行请求的方法叫做行动方法。
源代码
package Command /** * 接收者角色类 * */ class Receiver { /* * 真正执行命令相产操作 * */ fun action(){ println("执行操作") } }
package Command /** * 抽象命令角色类 * */ interface Command { /* * 执行方法 * */ fun execute() }
package Command /** * 具体命令角色类 * */ class ConcreteCommand constructor(receiver: Receiver) : Command { //持有相应的接收者对象 private var receiver: Receiver? = null init { this.receiver = receiver } override fun execute() { //通常会转调接收者对象的相应方法,让接收者来真正执行功能 receiver?.action() } }
package Command /** * 请求者角色类 * */ class Invoker constructor(command: Command) { //持有命令对象 private var command: Command? = null init { this.command = command } /** * 行动方法 * */ fun action(){ command?.execute() } }
测试
//创建接收者 val receiver = Receiver() //创建命令对象,设定他的接收者 val command = ConcreteCommand(receiver) //创建请求者,把命令对象设置进去 val invoker = Invoker(command) //执行方法 invoker.action()
命令模式的注意事项和细节
- 将发起请求的对象与执行请求的对象解耦。发起请求的对象是调用者,调用者只要调用命令对象的execute()方法就可以让接收者工作,而不必知道具体的接收者对象是谁、是如何实现的,命令对象会负责让接收者执行请求的动作,也是就是说请求发起者和请求执行者之间的解耦是通过命令对象实现的,命令对象起到了纽带桥梁的作用。
- 容易设计一个命令队列。只要把命令对象放到队列,就可以多线程的执行命令。
- 容易实现对请求的撤销和重做。
- 命令模式的不足:可能导致某些系统有过多的具体命令类,增加了系统的复杂度,这点在使用时要注意。
- 空命令也是一种设计模式,它为我们省去了判空的操作。
- 命令模式经典的应用场景:界面的一个按钮都是一条命令、模拟cmd(dos命令)订单的撤销/恢复、触发-反馈机制。