Command
别名:动作、Action、事务、Transaction
定义
它可将请求转换为一个包 含与请求相关的所有信息 的独立对象。该转换让你能根据不同的请求将方法 参数化、延迟请求执行或 将其放入队列中,且能实现可撤销操作。
应用场景
1.通过操作来参数化对象
命令模式可将特定的方法调用转化为独立对象。 这一改变也带来了许多有趣的应用: 你可以将命令作为方法的参数进行 传递、 将命令保存在其他对象中, 或者在运行时切换已连接 的命令等。【 你正在开发一个 GUI 组件(例如上下文菜单), 你希望用户能够配置菜单项, 并在点击菜单项时触发操作。】
2.将操作放入队列中、操作的执行或者远程执行操作
同其他对象一样, 命令也可以实现序列化(序列化的意思是 转化为字符串), 从而能方便地写入文件或数据库中。 一段 时间后, 该字符串可被恢复成为最初的命令对象。 因此, 你 可以延迟或计划命令的执行。 但其功能远不止如此! 使用同样的方式, 你还可以将命令放入队列、 记录命令或者通过网络发送命令。
3.实现操作回滚功能
尽管有很多方法可以实现撤销和恢复功能,但命令模式可能是其中最常用的一种。
实现方式
1.声明仅有一个执行方法的命令接口。
2.抽取请求并使之成为实现命令接口的具体命令类。 每个类都 必须有一组成员变量来保存请求参数和对于实际接收者对象 的引用。 所有这些变量的数值都必须通过命令构造函数进行 初始化。
3.找到担任发送者职责的类。 在这些类中添加保存命令的成员 变量。 发送者只能通过命令接口与其命令进行交互。 发送者 自身通常并不创建命令对象, 而是通过客户端代码获取。
4.修改发送者使其执行命令, 而非直接将请求发送给接收者。
5.客户端必须按照以下顺序来初始化对象:
◦ 创建接收者。 ◦ 创建命令,如有需要可将其关联至接收者。 ◦ 创建发送者并将其与特定命令关联。
优缺点
优点:
1.单一职责原则。 你可以解耦触发和执行操作的类。
2.开闭原则。 你可以在不修改已有客户端代码的情况下在程序 中创建新的命令。
3.你可以实现撤销和恢复功能。
4.你可以实现操作的延迟执行。
5.你可以将一组简单命令组合成一个复杂命令。
缺点:
1.代码可能会变得更加复杂, 因为你在发送者和接收者之间增 加了一个全新的层次。
结构
UML图
classDiagram
Client ..>Command1
Client -->Invoker
Invoker o--> Command
Command1 ..> Command
Command1 o--|> Receiver
class Invoker{
-command
+setCommand(command)
+executeCommand()
}
class Command{
<<interface>>
+execute()
}
class Receiver{
...
+operation(a,b,c)
}
class Command1{
-receiver
-params
+Command1(receiver,params)
+execute()
}
参与者
1.发送者(Sender)—— 亦 称 “触发者 (Invoker)”—— 类负责对请求进行初始化, 其中必须包含一个成员变量来存储对于命令对象的引用。 发送者触发命令, 而不向接收者直接发送请求。 注意,发送者并不负责创建命令对象: 它通常会通过构造函数从客户端处获得预先生成的命令。
2.命令(Command)接口通常仅声明一个执行命令的方法。
3.具体命令(Concrete Commands) 会实现各种类型的请求。 具体命令自身并不完成工作, 而是会将调用委派给一个业务 逻辑对象。 但为了简化代码, 这些类可以进行合并。
接收对象执行方法所需的参数可以声明为具体命令的成员变 量。 你可以将命令对象设为不可变, 仅允许通过构造函数对 这些成员变量进行初始化。
4.接收者(Receiver)类包含部分业务逻辑。几乎任何对象都可以作为接收者。 绝大部分命令只处理如何将请求传递到接 收者的细节, 接收者自己会完成实际的工作。
5.客户端(Client)会创建并配置具体命令对象。客户端必须 将包括接收者实体在内的所有请求参数传递给命令的构造函 数。 此后, 生成的命令就可以与一个或多个发送者相关联了。
通用写法
public class Receiver {
public void action(){
System.out.println("执行相关操作");
}
}
public interface Command {
void execute();
}
public class ConcreteCommand implements Command{
//直接创建对象 不暴露给客户端
private Receiver receiver = new Receiver();
public void execute(){
this.receiver.action();
}
}
public class Invoker {
private Command command;
public Invoker(Command command){
this.command = command;
}
public void action(){
command.execute();;
}
}
public class Client {
public static void main(String[] args) {
Command command = new ConcreteCommand();
Invoker invoker = new Invoker(command);
invoker.action();
}
}