Skip to content

命令模式

引言

在软件交互中,当操作需要参数化处理(如支持撤销/重做)或解耦调用者与执行者时(如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事件处理到分布式事务,命令模式以灵活的对象结构支撑复杂操作控制,成为软件设计中不可或缺的"操作指挥官"。