Java设计模式-命令模式

Java设计模式-命令模式

模式概述

命令模式简介

核心思想:将"请求"封装为独立的命令对象,使请求的发送者(调用者)与接收者(执行者)完全解耦。通过命令对象的统一接口,支持请求的参数化、队列化、日志记录及撤销/重做等扩展操作。

模式类型:行为型设计模式(关注对象间的交互与职责分配)。

作用

  • 解耦发送者与接收者:调用者只需持有命令对象,无需直接依赖具体接收者。
  • 支持灵活扩展:新增请求类型仅需添加新的命令类,符合"开闭原则"。
  • 实现复杂控制:通过命令队列、日志记录或事务管理,支持批量执行、撤销/重做等功能。

典型应用场景

  • GUI事件处理(如按钮点击触发菜单打开、窗口关闭等操作)。
  • 事务管理系统(如数据库事务的提交/回滚,通过命令对象记录操作步骤)。
  • 智能设备控制(如遥控器控制空调、电视,通过命令对象统一管理多设备操作)。
  • 消息队列(如将用户请求封装为命令对象,异步执行或持久化存储)。

我认为:命令模式是"将动作打包成对象"的艺术,让请求的发起者专注于"触发",执行者专注于"实现",中间通过命令对象灵活调度。

课程目标

  • 理解命令模式的核心思想和经典应用场景
  • 识别应用场景,使用命令模式解决功能要求
  • 了解命令模式的优缺点

核心组件

角色-职责表

角色 职责 示例类名
命令接口(Command) 定义执行请求的统一接口(如execute() Command
具体命令(ConcreteCommand) 实现命令接口,绑定具体接收者并实现执行逻辑 LightOnCommandLightOffCommand
调用者(Invoker) 持有命令对象,触发命令执行(如遥控器按钮) RemoteController
接收者(Receiver) 实际执行请求的对象(如电灯、空调等具体设备) LightAirConditioner

类图

下面是一个简化的类图表示,展示了命令模式中的主要角色及其交互方式:
实现 实现 依赖 使用 使用 <<interface>> Command +execute() +undo() LightOnCommand -Light receiver +execute() +undo() LightOffCommand -Light receiver +execute() +undo() RemoteController -Command currentCommand +setCommand(Command) +pressButton() +pressUndoButton() Light +turnOn() +turnOff()


传统实现 VS 命令模式

案例需求

案例背景:实现一个遥控器控制电灯的功能,支持"开灯"和"关灯"操作,后续可能扩展"调节亮度""定时开关"等新功能。

传统实现(痛点版)

代码实现

java 复制代码
// 传统实现:调用者直接依赖接收者
class Light {
    public void turnOn() {
        System.out.println("电灯:打开");
    }
    public void turnOff() {
        System.out.println("电灯:关闭");
    }
}

// 遥控器直接调用Light的方法(强耦合)
class RemoteController {
    private Light light;

    public RemoteController(Light light) {
        this.light = light;
    }

    public void openLight() {
        light.turnOn(); // 直接调用接收者方法
    }

    public void closeLight() {
        light.turnOff(); // 直接调用接收者方法
    }
}

// 使用示例
public class Client {
    public static void main(String[] args) {
        Light light = new Light();
        RemoteController remote = new RemoteController(light);
        remote.openLight();  // 电灯:打开
        remote.closeLight(); // 电灯:关闭
    }
}

痛点总结

  • 强耦合:遥控器(调用者)与电灯(接收者)直接绑定,新增操作(如调节亮度)需修改遥控器代码,违反开闭原则。
  • 功能扩展困难:无法统一管理多个操作(如批量执行"开灯+关空调"),也不支持撤销/重做。
  • 缺乏抽象:请求(开灯/关灯)未被封装为对象,难以进行队列调度或日志记录。

命令模式 实现(优雅版)

代码实现

java 复制代码
// 1. 命令接口(定义执行和撤销方法)
interface Command {
    void execute();  // 执行命令
    void undo();     // 撤销命令(可选)
}

// 2. 具体命令:开灯命令(绑定接收者Light)
class LightOnCommand implements Command {
    private Light receiver;

    public LightOnCommand(Light receiver) {
        this.receiver = receiver;
    }

    @Override
    public void execute() {
        receiver.turnOn(); // 调用接收者的实际操作
    }

    @Override
    public void undo() {
        receiver.turnOff(); // 撤销操作是关灯
    }
}

// 3. 具体命令:关灯命令
class LightOffCommand implements Command {
    private Light receiver;

    public LightOffCommand(Light receiver) {
        this.receiver = receiver;
    }

    @Override
    public void execute() {
        receiver.turnOff();
    }

    @Override
    public void undo() {
        receiver.turnOn(); // 撤销操作是开灯
    }
}

// 4. 调用者:遥控器(支持设置和触发命令)
class RemoteController {
    private Command currentCommand; // 当前选中的命令

    public void setCommand(Command cmd) {
        this.currentCommand = cmd;
    }

    public void pressButton() {
        currentCommand.execute(); // 触发命令执行
    }

    public void pressUndoButton() {
        currentCommand.undo(); // 触发撤销操作
    }
}

// 5. 接收者:电灯(实际执行操作)
class Light {
    public void turnOn() {
        System.out.println("电灯:打开");
    }
    public void turnOff() {
        System.out.println("电灯:关闭");
    }
}

// 使用示例
public class Client {
    public static void main(String[] args) {
        Light light = new Light();
        Command lightOn = new LightOnCommand(light);
        Command lightOff = new LightOffCommand(light);

        RemoteController remote = new RemoteController();

        remote.setCommand(lightOn);
        remote.pressButton();  // 输出:电灯:打开

        remote.setCommand(lightOff);
        remote.pressButton();  // 输出:电灯:关闭

        // 支持撤销(例如:误操作后回退)
        remote.setCommand(lightOn);
        remote.pressButton();  // 电灯:打开
        remote.pressUndoButton(); // 输出:电灯:关闭
    }
}

优势

  • 解耦调用者与接收者 :遥控器(RemoteController)仅依赖Command接口,无需知道Light的具体实现。
  • 灵活扩展 :新增"调节亮度"功能只需添加BrightnessAdjustCommand类,无需修改现有代码。
  • 支持复杂控制:通过命令队列(如批量执行多个命令)或日志记录(如持久化命令对象),可实现事务回滚、撤销/重做等功能。

局限

  • ​类数量增加​:每个请求需定义一个具体命令类,可能导致系统类膨胀(可通过宏命令或组合模式优化)。
  • 过度设计风险:若需求简单(如仅需直接调用),引入命令模式会增加代码复杂度。

模式变体

  • 宏命令(Macro Command):将多个命令组合成一个复合命令(如"一键回家"场景:开灯+开空调+播放音乐),通过一次触发执行所有子命令。
  • 带事务的命令 :记录命令执行前的状态(如数据库快照),通过undo()方法回滚到之前的状态,支持事务的原子性。
  • 异步命令:将命令对象放入线程池或消息队列异步执行(如电商系统中的"下单"请求,通过命令队列解耦前端与库存/支付服务)。
  • 日志命令:将命令序列化后持久化存储(如日志文件),用于系统崩溃后的恢复或操作审计。

最佳实践

建议 理由
命令接口保持简洁 仅定义execute()等核心方法,避免冗余方法增加实现复杂度。
接收者职责单一 接收者应专注于执行具体操作(如Light仅处理电灯开关),不与命令逻辑耦合。
合理使用钩子方法 在命令接口中添加可选方法(如isEnabled()),允许子类控制是否执行。
命令对象无状态 若命令需频繁创建,可通过享元模式共享实例(如无状态的LightOnCommand)。
支持撤销时记录上下文 undo()需要依赖执行时的状态(如修改前的数值),命令对象需保存相关上下文。

一句话总结

命令模式通过将请求封装为对象,实现了调用者与接收者的解耦,同时支持灵活扩展、批量执行及撤销/重做等高级功能,是构建可维护、可扩展系统的重要工具。

如果关注Java设计模式内容,可以查阅作者的其他Java设计模式系列文章。😊