设计模式 - 命令模式

文章目录

  • [1. 生活中的例子](#1. 生活中的例子)
    • [1.1 餐厅点餐系统 🍽️](#1.1 餐厅点餐系统 🍽️)
    • [1.2 遥控器控制家电 📺](#1.2 遥控器控制家电 📺)
    • [1.3 游戏手柄 🎮](#1.3 游戏手柄 🎮)
  • [2. 代码中的例子](#2. 代码中的例子)
    • [2.1 灯光开关控制](#2.1 灯光开关控制)
    • [2.2 智能家居系统](#2.2 智能家居系统)
    • [2.3 支持队列和日志的命令模式](#2.3 支持队列和日志的命令模式)
  • [3. 归纳总结](#3. 归纳总结)
    • [3.1 核心要点总结](#3.1 核心要点总结)
    • [3.2 重要但未提及的知识点](#3.2 重要但未提及的知识点)
    • [3.3 实际应用建议](#3.3 实际应用建议)

1. 生活中的例子

1.1 餐厅点餐系统 🍽️

想象你去餐厅吃饭:

  • 你(客户端):想要吃牛排和沙拉
  • 服务员(调用者):拿着点餐单记录你的需求
  • 点餐单(命令对象):包含具体菜品和烹饪要求
  • 厨师(接收者):实际做菜的人

关键理解:你不需要直接跑到厨房告诉厨师怎么做,服务员也不需要知道具体烹饪方法。点餐单这个"命令对象"把请求和具体执行解耦了。

1.2 遥控器控制家电 📺

电视遥控器的工作方式:

  • 遥控器按钮(命令):比如"音量+""换台"
  • 遥控器(调用者):只知道按按钮,不知道内部原理
  • 电视(接收者):实际执行操作
  • 你(客户端):按按钮的人

核心思想:同样的遥控器可以控制不同品牌的电视,因为命令被抽象化了。

1.3 游戏手柄 🎮

游戏手柄设计:

  • A键、B键(命令):绑定不同游戏动作
  • 手柄(调用者):只负责触发命令
  • 游戏角色(接收者):执行具体动作
  • 按键配置(客户端):可以随时改变按键功能

设计优势:你可以随时改变按键映射,而不需要修改手柄硬件或游戏代码。

2. 代码中的例子

2.1 灯光开关控制

java 复制代码
// 1. 命令接口(所有命令的通用接口)
interface Command {
    void execute();
    void undo(); // 支持撤销操作
}

// 2. 接收者(实际执行操作的对象)
class Light {
    private boolean isOn = false;
    
    public void turnOn() {
        isOn = true;
        System.out.println("💡 灯已打开");
    }
    
    public void turnOff() {
        isOn = false;
        System.out.println("💡 灯已关闭");
    }
    
    public boolean isOn() {
        return isOn;
    }
}

// 3. 具体命令类
class LightOnCommand implements Command {
    private Light light;
    
    public LightOnCommand(Light light) {
        this.light = light;
    }
    
    @Override
    public void execute() {
        light.turnOn();
    }
    
    @Override
    public void undo() {
        light.turnOff();
    }
}

class LightOffCommand implements Command {
    private Light light;
    
    public LightOffCommand(Light light) {
        this.light = light;
    }
    
    @Override
    public void execute() {
        light.turnOff();
    }
    
    @Override
    public void undo() {
        light.turnOn();
    }
}

// 4. 调用者(触发命令的对象)
class RemoteControl {
    private Command command;
    private Command lastCommand; // 记录上次命令,用于撤销
    
    public void setCommand(Command command) {
        this.command = command;
    }
    
    public void pressButton() {
        lastCommand = command;
        command.execute();
    }
    
    public void pressUndo() {
        if (lastCommand != null) {
            lastCommand.undo();
            lastCommand = null;
        }
    }
}

// 5. 客户端使用
public class CommandPatternDemo {
    public static void main(String[] args) {
        // 创建接收者
        Light livingRoomLight = new Light();
        
        // 创建具体命令
        Command lightOn = new LightOnCommand(livingRoomLight);
        Command lightOff = new LightOffCommand(livingRoomLight);
        
        // 创建调用者
        RemoteControl remote = new RemoteControl();
        
        // 测试开灯
        System.out.println("=== 测试开灯 ===");
        remote.setCommand(lightOn);
        remote.pressButton();  // 输出:💡 灯已打开
        
        // 测试撤销
        System.out.println("\n=== 测试撤销 ===");
        remote.pressUndo();    // 输出:💡 灯已关闭
        
        // 测试关灯
        System.out.println("\n=== 测试关灯 ===");
        remote.setCommand(lightOff);
        remote.pressButton();  // 输出:💡 灯已关闭
    }
}

2.2 智能家居系统

java 复制代码
// 宏命令:一次执行多个命令
class MacroCommand implements Command {
    private List<Command> commands = new ArrayList<>();
    
    public void addCommand(Command command) {
        commands.add(command);
    }
    
    @Override
    public void execute() {
        System.out.println("🚀 执行宏命令序列:");
        for (Command cmd : commands) {
            cmd.execute();
        }
    }
    
    @Override
    public void undo() {
        System.out.println("↩️ 撤销宏命令序列:");
        // 反向执行撤销
        List<Command> reversed = new ArrayList<>(commands);
        Collections.reverse(reversed);
        for (Command cmd : reversed) {
            cmd.undo();
        }
    }
}

// 更多接收者类
class TV {
    private boolean isOn = false;
    private int volume = 20;
    
    public void turnOn() {
        isOn = true;
        System.out.println("📺 电视已打开,音量:" + volume);
    }
    
    public void turnOff() {
        isOn = false;
        System.out.println("📺 电视已关闭");
    }
    
    public void setVolume(int volume) {
        this.volume = volume;
        System.out.println("📺 电视音量设置为:" + volume);
    }
}

class AirConditioner {
    private boolean isOn = false;
    private int temperature = 25;
    
    public void turnOn(int temp) {
        isOn = true;
        this.temperature = temp;
        System.out.println("❄️ 空调已打开,温度:" + temp + "°C");
    }
    
    public void turnOff() {
        isOn = false;
        System.out.println("❄️ 空调已关闭");
    }
}

// 使用示例
public class SmartHomeDemo {
    public static void main(String[] args) {
        // 创建设备
        Light light = new Light();
        TV tv = new TV();
        AirConditioner ac = new AirConditioner();
        
        // 创建具体命令(使用匿名类简化)
        Command lightOn = () -> light.turnOn();
        Command tvOn = () -> {
            tv.turnOn();
            tv.setVolume(30);
        };
        Command acOn = () -> ac.turnOn(22);
        
        // 创建"回家模式"宏命令
        MacroCommand homeMode = new MacroCommand();
        homeMode.addCommand(lightOn);
        homeMode.addCommand(tvOn);
        homeMode.addCommand(acOn);
        
        // 执行回家模式
        System.out.println("=== 执行回家模式 ===");
        homeMode.execute();
        
        System.out.println("\n=== 撤销回家模式 ===");
        homeMode.undo();
    }
}

2.3 支持队列和日志的命令模式

java 复制代码
import java.util.LinkedList;
import java.util.Queue;

// 命令管理器(支持队列和日志)
class CommandManager {
    private Queue<Command> commandQueue = new LinkedList<>();
    private List<Command> commandHistory = new ArrayList<>();
    
    // 添加命令到队列
    public void addToQueue(Command command) {
        commandQueue.offer(command);
    }
    
    // 批量执行队列中的命令
    public void processQueue() {
        System.out.println("🔄 处理命令队列...");
        while (!commandQueue.isEmpty()) {
            Command cmd = commandQueue.poll();
            cmd.execute();
            commandHistory.add(cmd);
        }
    }
    
    // 显示执行历史
    public void showHistory() {
        System.out.println("📜 命令执行历史:");
        for (int i = 0; i < commandHistory.size(); i++) {
            System.out.println((i + 1) + ". " + commandHistory.get(i).getClass().getSimpleName());
        }
    }
}

// 延迟执行命令
class DelayedCommand implements Command {
    private Command command;
    private long delayMillis;
    
    public DelayedCommand(Command command, long delayMillis) {
        this.command = command;
        this.delayMillis = delayMillis;
    }
    
    @Override
    public void execute() {
        try {
            System.out.println("⏳ 等待 " + delayMillis + "ms 后执行...");
            Thread.sleep(delayMillis);
            command.execute();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    @Override
    public void undo() {
        command.undo();
    }
}

// 使用示例
public class AdvancedCommandDemo {
    public static void main(String[] args) {
        Light light = new Light();
        CommandManager manager = new CommandManager();
        
        // 创建各种命令
        Command cmd1 = new LightOnCommand(light);
        Command cmd2 = new DelayedCommand(new LightOffCommand(light), 1000);
        Command cmd3 = () -> System.out.println("🔔 自定义命令:播放门铃声音");
        
        // 添加到队列
        manager.addToQueue(cmd1);
        manager.addToQueue(cmd2);
        manager.addToQueue(cmd3);
        
        // 处理队列
        manager.processQueue();
        
        // 查看历史
        manager.showHistory();
    }
}

3. 归纳总结

3.1 核心要点总结

  1. 四大角色

    • Command(命令):定义执行操作的接口
    • ConcreteCommand(具体命令):绑定接收者和动作
    • Invoker(调用者):请求的发送者
    • Receiver(接收者):知道如何执行操作
  2. 三大优势

    • 解耦:调用者与接收者完全独立
    • 可扩展:新命令很容易添加
    • 支持高级功能:撤销、重做、队列、日志等
  3. 两大应用场景:

    • 需要将操作参数化的场景
    • 需要支持撤销/重做、事务处理的场景

3.2 重要但未提及的知识点

  1. 命令模式 vs 策略模式
    • 命令模式:关注"做什么"和"什么时候做"
    • 策略模式:关注"怎么做"
  2. 命令对象的生命周期
    • 命令对象可以是一次性的,也可以是可重用的
    • 考虑使用对象池管理频繁创建的命令对象

3.线程安全问题

java 复制代码
// 线程安全的命令管理器
class ThreadSafeCommandManager {
    private final Queue<Command> queue = new ConcurrentLinkedQueue<>();
    
    public synchronized void addCommand(Command cmd) {
        queue.offer(cmd);
    }
}
  1. 结合其他模式
    • 组合模式:创建宏命令(复合命令)
    • 备忘录模式:实现更复杂的撤销/重做
    • 原型模式:克隆命令对象

3.3 实际应用建议

  1. 何时使用

    • GUI 中的按钮和菜单项
    • 事务系统(数据库操作)
    • 任务调度系统
    • 网络请求队列
  2. 性能考虑

java 复制代码
// 使用享元模式共享接收者
class CommandFactory {
    private static Map<String, Command> commandPool = new HashMap<>();
    
    public static Command getCommand(String key, Receiver receiver) {
        return commandPool.computeIfAbsent(key, 
            k -> new ConcreteCommand(receiver));
    }
}
  1. 现代Java特性优化
java 复制代码
// 使用函数式接口简化
@FunctionalInterface
interface SimpleCommand {
    void execute();
    
    default SimpleCommand andThen(SimpleCommand after) {
        return () -> {
            execute();
            after.execute();
        };
    }
}

// 使用示例
SimpleCommand cmd = () -> System.out.println("Hello")
    .andThen(() -> System.out.println("World"));
相关推荐
雨中飘荡的记忆1 小时前
设计模式之门面模式详解
microsoft·设计模式
明洞日记1 小时前
【设计模式手册016】中介者模式 - 解耦多对象交互
c++·设计模式·交互·中介者模式
开心香辣派小星1 小时前
23种设计模式-19策略模式(Strategy Pattern)
java·设计模式·策略模式
开心香辣派小星1 天前
23种设计模式-17备忘录模式
java·设计模式·备忘录模式
开心香辣派小星1 天前
23种设计模式-11代理模式
设计模式·代理模式
雨中飘荡的记忆1 天前
设计模式之原型模式详解
设计模式·原型模式
@小白鸽1 天前
1.2.1创建型设计模式
开发语言·设计模式
开心香辣派小星1 天前
23种设计模式-18观察者(Observer)模式
java·开发语言·设计模式
开心香辣派小星1 天前
23种设计模式-16中介者模式
设计模式·中介者模式