前言

Github:https://github.com/HealerJean

博客:http://blog.healerjean.com

一、命令模式(Command Pattern)

1. 模式概述

命令模式 是一种 行为型设计模式,它将 一个请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。

核心思想“把动作变成对象。”

命令模式 = 请求对象化 + 行为参数化 + 操作可追溯

  • 它不是“把方法包装一下”,而是“让动作具备生命”。

类比理解:

  • 餐厅点餐:顾客(Client)点菜 → 服务员(Invoker)记录订单(Command)→ 厨师(Receiver)做菜; 订单是独立对象,可存档、取消、批量处理。
  • 你提到的军事场景:司令员(Client)下达“进攻”命令 → 传令兵(Invoker)传递命令对象 → 士兵(Receiver)执行;

2. 使用场景

命令模式适用于以下情况:

  • 需要 参数化对象行为(用不同命令配置按钮、菜单);
  • 需要 支持撤销(Undo)/重做(Redo) 操作;
  • 需要 将请求放入队列(如任务调度、线程池);
  • 需要 记录操作日志(用于事务回滚或审计);
  • 需要 组合多个命令为宏命令(Macro Command)。

典型应用

  • GUI 菜单/按钮的点击事件(每个按钮绑定一个命令);
  • 文本编辑器的撤销功能;
  • 游戏中的技能系统(每个技能是一个命令);
  • 事务性操作(如数据库事务回滚)。

3. 示例程序:军事指挥系统(优化版)

1)命令接口 Command

/**
 * 命令接口(Command)
 */
public interface Command {
    void execute();      // 执行
    void undo();         // 撤销(可选,但常用)
}

2)接收者 Soldier

/**
 * 接收者(Receiver)
 * 真正执行动作的对象
 */
public class Soldier {
    private String actionHistory = "";

    public void attack(String target) {
        System.out.println("【士兵】正在攻击目标: " + target);
        actionHistory = "attack:" + target;
    }

    public void defend() {
        System.out.println("【士兵】进入防御状态");
        actionHistory = "defend";
    }

    public void recall() {
        System.out.println("【士兵】撤销上一次操作: " + actionHistory);
    }
}

3)具体命令类

a、攻击命令

public class AttackCommand implements Command {
    private final Soldier soldier;
    private final String target;

    public AttackCommand(Soldier soldier, String target) {
        this.soldier = soldier;
        this.target = target;
    }

    @Override
    public void execute() {
        soldier.attack(target);
    }

    @Override
    public void undo() {
        soldier.recall();
    }
}

b、防御命令

public class DefendCommand implements Command {
    private final Soldier soldier;

    public DefendCommand(Soldier soldier) {
        this.soldier = soldier;
    }

    @Override
    public void execute() {
        soldier.defend();
    }

    @Override
    public void undo() {
        soldier.recall();
    }
}

4)调用者 General

import java.util.ArrayDeque;
import java.util.Deque;

/**
 * 调用者(Invoker)
 * 可支持命令队列和撤销栈
 */
public class General {
    private final Deque<Command> history = new ArrayDeque<>();

    public void issueCommand(Command command) {
        command.execute();
        history.push(command); // 记录历史,用于撤销
    }

    public void undoLastCommand() {
        if (!history.isEmpty()) {
            Command last = history.pop();
            last.undo();
        } else {
            System.out.println("【司令员】无可撤销的命令");
        }
    }
}

5)客户端测试 Main

  • 命令可撤销;
  • InvokerGeneral)完全不知道 Soldier 的存在;
  • 新增命令(如 RetreatCommand)无需修改现有代码。
public class Main {
    public static void main(String[] args) {
        Soldier soldier = new Soldier();
        General general = new General();

        // 下达命令
        general.issueCommand(new AttackCommand(soldier, "敌方碉堡"));
        general.issueCommand(new DefendCommand(soldier));

        System.out.println("\n--- 撤销上一条命令 ---");
        general.undoLastCommand();

        System.out.println("\n--- 再次撤销 ---");
        general.undoLastCommand();
    }
}

输出:

【士兵】正在攻击目标: 敌方碉堡
【士兵】进入防御状态

--- 撤销上一条命令 ---
【士兵】撤销上一次操作: defend

--- 再次撤销 ---
【士兵】撤销上一次操作: attack:敌方碉堡

6)模式角色

  • 解耦Invoker 不知道 Receiver 是谁;
  • 可扩展:新增命令只需实现 Command 接口;
  • 可组合:多个命令可组成复合命令。
角色 职责 示例
Command(命令接口) 声明执行操作的接口 Command
ConcreteCommand(具体命令) 绑定接收者,实现 execute() AttackCommand
Receiver(接收者) 真正执行业务逻辑的对象 Soldier
Invoker(调用者) 持有命令对象,触发执行(不关心具体逻辑) General / Button
Client(客户端) 创建命令对象,并设置接收者 应用启动时绑定命令

4、FQA

1)模式优点

  • 解耦调用者与接收者
  • 支持命令的排队、日志、撤销、重做
  • 易于扩展新命令(符合开闭原则);
  • 可组合命令(如宏命令:一键执行“攻击+防御”)。

2)注意事项

  • 类数量增加(每个命令一个类);
  • 简单场景可能过度设计
  • 撤销操作需 Receiver 支持状态回滚