命令模式
引言
在软件交互中,当操作需要参数化处理(如支持撤销/重做)或解耦调用者与执行者时(如GUI按钮触发业务逻辑),直接调用会导致代码僵化。命令模式通过将请求封装为独立对象,实现"请求即对象"的范式,成为行为设计中的关键解耦器。
诞生背景
GoF在《设计模式》中提出命令模式,解决三大痛点:
- 调用者与执行者强耦合:按钮代码直接调用业务逻辑
- 请求不可扩展:难以支持撤销、队列、日志等操作
- 动态行为受限:无法实现宏命令(批量操作)
演进过程
- GoF基础(1994):确立核心角色(Invoker、Command、Receiver)
- 事务系统演进:数据库事务管理(commit/rollback)
- 现代应用:
- 前端Redux的Action-Command映射
- 微服务的CQRS(命令查询职责分离)
核心概念
- 命令(Command):封装请求的对象(含
execute()方法) - 调用者(Invoker):触发命令执行(如按钮/菜单)
- 接收者(Receiver):实际执行操作的对象
- 客户端(Client):创建命令并绑定接收者
通用实现
Java 实现
java
// 接收者:实际执行操作
class Light {
public void on() { System.out.println("Light ON"); }
public void off() { System.out.println("Light OFF"); }
}
// 命令接口
interface Command {
void execute();
}
// 具体命令
class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
}
// 调用者(遥控器按钮)
class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Light light = new Light();
Command lightOn = new LightOnCommand(light);
RemoteControl remote = new RemoteControl();
remote.setCommand(lightOn);
remote.pressButton(); // 输出: Light ON
}
}PHP 实现
php
// 接收者:文档编辑器
class Document {
public function save(): void {
echo "Document saved!\n";
}
public function copy(): void {
echo "Content copied to clipboard\n";
}
}
// 命令接口
interface Command {
public function execute(): void;
}
// 具体命令
class SaveCommand implements Command {
private Document $doc;
public function __construct(Document $doc) {
$this->doc = $doc;
}
public function execute(): void {
$this->doc->save();
}
}
// 调用者(工具栏按钮)
class ToolbarButton {
private Command $command;
public function setCommand(Command $command): void {
$this->command = $command;
}
public function click(): void {
$this->command->execute();
}
}
// 客户端
$doc = new Document();
$saveCmd = new SaveCommand($doc);
$button = new ToolbarButton();
$button->setCommand($saveCmd);
$button->click(); // 输出: Document saved!应用场景
- 操作队列:线程池任务调度
- 撤销/重做:编辑器历史记录
- GUI事件处理:按钮/菜单动作绑定
- 分布式事务:命令组合(如转账:扣款+入账)
案例:智能家居遥控器
Java 实现
java
// 宏命令(批量操作)
class MacroCommand implements Command {
private List<Command> commands = new ArrayList<>();
public void add(Command cmd) {
commands.add(cmd);
}
@Override
public void execute() {
for (Command cmd : commands) {
cmd.execute();
}
}
}
// 客户端使用
Light light = new Light();
TV tv = new TV(); // 假设TV类有on()方法
Command lightOn = new LightOnCommand(light);
Command tvOn = new TVOnCommand(tv);
MacroCommand partyMode = new MacroCommand();
partyMode.add(lightOn);
partyMode.add(tvOn);
RemoteControl remote = new RemoteControl();
remote.setCommand(partyMode);
remote.pressButton(); // 同时打开灯和电视PHP 实现
php
// 接收者:空调
class AirConditioner {
public function on(): void { echo "AC ON\n"; }
public function setTemp(int $temp): void {
echo "AC set to {$temp}°C\n";
}
}
// 具体命令:设置温度
class SetTempCommand implements Command {
private AirConditioner $ac;
private int $temp;
public function __construct(AirConditioner $ac, int $temp) {
$this->ac = $ac;
$this->temp = $temp;
}
public function execute(): void {
$this->ac->setTemp($this->temp);
}
}
// 客户端使用
$ac = new AirConditioner();
$acOnCmd = new class($ac) implements Command { // PHP匿名类命令
public function __construct(private AirConditioner $ac) {}
public function execute(): void { $this->ac->on(); }
};
$setTempCmd = new SetTempCommand($ac, 22);
$remote = new ToolbarButton();
$remote->setCommand($acOnCmd);
$remote->click(); // AC ON
$remote->setCommand($setTempCmd);
$remote->click(); // AC set to 22°C优点
- 解耦调用者与执行者:Invoker无需知道Receiver细节
- 支持扩展操作:轻松添加新命令
- 实现高阶功能:撤销、重做、事务
- 组合命令:支持宏命令(批量操作)
缺点
- 类膨胀:每个操作需单独命令类
- 过度设计风险:简单场景增加复杂度
- 执行层级加深:可能影响性能
扩展
- 撤销机制:命令对象存储状态(
unexecute()方法) - 命令队列:线程池按顺序执行命令对象
- 日志命令:持久化命令序列实现系统恢复
- 智能命令:自主决策是否执行(如余额不足时拒绝转账)
模式协作
- 与备忘录模式:实现命令撤销(存储对象历史状态)
- 与组合模式:构建宏命令(批量执行子命令)
- 与责任链模式:实现命令的过滤与传递
- 与原型模式:克隆命令对象实现重做
延伸思考
- CQRS架构:命令(写操作)与查询(读操作)分离
- 事件溯源:存储命令序列而非最终状态(如银行交易流水)
- 前端应用:Redux的Action本质是命令模式实践
- 微服务补偿事务:失败时执行反向命令(如订单取消→库存回滚)
总结
命令模式是行为封装的艺术家,通过将请求转化为独立对象,实现调用与执行的彻底解耦。其核心价值在于:操作的对象化与行为的可管理性。从GUI事件处理到分布式事务,命令模式以灵活的对象结构支撑复杂操作控制,成为软件设计中不可或缺的"操作指挥官"。