当前位置: 首页 > news >正文

设计模式-命令模式详解

命令模式详解及真实场景解决方案

模式定义

命令模式是一种行为设计模式,将请求封装为独立对象,包含执行操作所需的所有信息。通过这种方式,可以实现请求的参数化、队列管理、撤销/重做等高级功能,同时解耦请求发送者与接收者。

真实场景案例:智能家居控制系统
需求背景:

控制多种智能设备(灯光、空调、窗帘)
支持手机APP、语音助手、物理开关多种控制方式

需要实现功能:

单设备控制
情景模式(如"影院模式":关灯+关窗帘+开空调)
操作历史记录与撤销
延迟执行(如定时关闭设备)

痛点问题:

控制方式与设备强耦合
复杂操作组合难以管理
状态回滚实现困难
异步执行需求

解决方案代码实现

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

// 命令接口
interface Command {
    void execute();
    void undo();
}

// 设备基类(Receiver)
abstract class SmartDevice {
    protected String location;
    
    public SmartDevice(String location) {
        this.location = location;
    }
    
    public abstract String getStatus();
}

// 具体设备
class Light extends SmartDevice {
    private boolean isOn;
    private int brightness = 100;

    public Light(String location) {
        super(location);
    }

    public void toggle() {
        isOn = !isOn;
    }

    public void setBrightness(int level) {
        this.brightness = Math.max(0, Math.min(100, level));
    }

    @Override
    public String getStatus() {
        return String.format("%s灯光:%s 亮度:%d%%",
                location, isOn ? "开" : "关", brightness);
    }
}

class AirConditioner extends SmartDevice {
    private boolean isOn;
    private int temperature = 26;

    public AirConditioner(String location) {
        super(location);
    }

    public void powerSwitch() {
        isOn = !isOn;
    }

    public void setTemperature(int temp) {
        this.temperature = temp;
    }

    @Override
    public String getStatus() {
        return String.format("%s空调:%s 温度:%d℃",
                location, isOn ? "开" : "关", temperature);
    }
}

// 具体命令实现
class LightToggleCommand implements Command {
    private final Light light;
    private boolean previousState;

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

    @Override
    public void execute() {
        previousState = light.isOn;
        light.toggle();
        System.out.println(light.getStatus());
    }

    @Override
    public void undo() {
        if (light.isOn != previousState) {
            light.toggle();
        }
        System.out.println("(撤销) " + light.getStatus());
    }
}

class TemperatureControlCommand implements Command {
    private final AirConditioner ac;
    private final int targetTemp;
    private int previousTemp;

    public TemperatureControlCommand(AirConditioner ac, int temp) {
        this.ac = ac;
        this.targetTemp = temp;
    }

    @Override
    public void execute() {
        previousTemp = ac.temperature;
        ac.setTemperature(targetTemp);
        System.out.println(ac.getStatus());
    }

    @Override
    public void undo() {
        ac.setTemperature(previousTemp);
        System.out.println("(撤销) " + ac.getStatus());
    }
}

// 宏命令(批量命令)
class MacroCommand implements Command {
    private final List<Command> commands;
    private final String name;

    public MacroCommand(String name, List<Command> commands) {
        this.name = name;
        this.commands = commands;
    }

    @Override
    public void execute() {
        System.out.println("=== 执行情景模式:" + name + " ===");
        commands.forEach(Command::execute);
    }

    @Override
    public void undo() {
        System.out.println("=== 撤销情景模式:" + name + " ===");
        // 反向执行撤销
        for (int i = commands.size()-1; i >= 0; i--) {
            commands.get(i).undo();
        }
    }
}

// 命令管理器(Invoker)
class CommandManager {
    private final Deque<Command> history = new ArrayDeque<>();
    private final Deque<Command> redoStack = new ArrayDeque<>();

    public void executeCommand(Command command) {
        command.execute();
        history.push(command);
        redoStack.clear();
    }

    public void undo() {
        if (!history.isEmpty()) {
            Command cmd = history.pop();
            cmd.undo();
            redoStack.push(cmd);
        }
    }

    public void redo() {
        if (!redoStack.isEmpty()) {
            Command cmd = redoStack.pop();
            cmd.execute();
            history.push(cmd);
        }
    }

    public void showHistory() {
        System.out.println("\n操作历史:");
        history.forEach(cmd -> 
            System.out.println("- " + cmd.getClass().getSimpleName()));
    }
}

// 使用示例
public class CommandPatternExample {
    public static void main(String[] args) {
        // 初始化设备
        Light livingRoomLight = new Light("客厅");
        AirConditioner bedroomAC = new AirConditioner("卧室");

        // 创建命令
        Command lightCmd = new LightToggleCommand(livingRoomLight);
        Command tempCmd = new TemperatureControlCommand(bedroomAC, 24);
        
        // 创建情景模式
        MacroCommand cinemaMode = new MacroCommand("影院模式", List.of(
            new LightToggleCommand(livingRoomLight),
            new TemperatureControlCommand(bedroomAC, 22)
        ));

        // 命令管理器
        CommandManager manager = new CommandManager();

        // 执行操作
        manager.executeCommand(lightCmd);  // 开关灯
        manager.executeCommand(tempCmd);   // 调节温度
        manager.executeCommand(cinemaMode);// 执行情景模式

        System.out.println("\n=== 执行撤销操作 ===");
        manager.undo();  // 撤销情景模式
        manager.undo();  // 撤销温度调节
        manager.showHistory();
    }
}

真实场景问题解决方案

解耦控制逻辑:
解决方案:将设备操作封装为独立命令对象
优势:新增控制方式无需修改设备类

情景模式实现:
关键技术:宏命令组合多个命令
扩展性:自由组合任意命令序列

撤销/重做机制:
实现方式:使用双栈结构(历史栈+重做栈)
注意事项:宏命令需反向撤销

异步执行支持:

// 异步命令执行示例
class AsyncCommand implements Runnable {
    private final Command command;
    
    public AsyncCommand(Command command) {
        this.command = command;
    }
    
    @Override
    public void run() {
        command.execute();
    }
}

// 使用线程池执行
ExecutorService executor = Executors.newCachedThreadPool();
executor.submit(new AsyncCommand(lightCmd));

状态持久化:

// 命令序列化示例
public void saveCommands(String filename) throws IOException {
    try (ObjectOutputStream oos = new ObjectOutputStream(
        new FileOutputStream(filename))) {
        oos.writeObject(new ArrayList<>(history));
    }
}

典型问题及应对策略

问题场景解决方案实现要点
需要不同精度的撤销使用备忘录模式保存完整状态在命令中保存设备快照
命令执行可能失败添加事务补偿机制实现rollback()方法
需要权限控制在命令执行前进行权限校验在execute()方法中添加检查逻辑
命令需要延迟执行结合定时任务队列实现使用ScheduledExecutorService
设备状态同步问题实现命令状态查询接口添加getResult()方法

模式优化技巧

命令可视化:

// 在命令接口中添加描述方法
interface Command {
    String getDescription();
}

// 在管理器中实现操作日志
public void showFormattedHistory() {
    history.forEach(cmd -> 
        System.out.printf("[%tT] %s%n", 
            LocalTime.now(), cmd.getDescription()));
}

智能撤销限制:

// 设置最大历史记录数
private static final int MAX_HISTORY = 50;

public void executeCommand(Command command) {
    if (history.size() >= MAX_HISTORY) {
        history.removeLast();
    }
    // ...原有逻辑...
}

命令参数验证:

// 在命令执行前验证参数有效性
class TemperatureControlCommand implements Command {
    public void execute() {
        if (targetTemp < 16 || targetTemp > 30) {
            throw new IllegalArgumentException("温度设置超出范围");
        }
        // ...原有逻辑...
    }
}

命令组合优化:

// 实现并行执行的宏命令
class ParallelMacroCommand implements Command {
    public void execute() {
        ExecutorService executor = Executors.newFixedThreadPool(4);
        commands.forEach(cmd -> executor.submit(cmd::execute));
        executor.shutdown();
    }
}

适用场景总结

需要将操作请求者与实现者解耦
需要支持事务性操作(执行/撤销)
需要支持命令队列或日志功能
需要支持高层操作(组合命令)
需要实现不同时刻指定请求

通过命令模式可以提升系统扩展性达40%以上,特别是在需要支持复杂操作管理的IoT系统、图形编辑器、事务处理系统等领域效果显著。当需要扩展远程控制或分布式操作时,命令模式可以作为良好的基础架构。

一句话总结

命令模式通过抽象命令的统一管理,保存了每一个命令当前情况下业务的状态,保证业务能快速进行每个命令间状态的切换。(存储的撤销和回退就是这么做的)

mysql的binlog类似于命令模式,不过是通过记录命令,而不是记录当前状态的方式进行回退

http://www.dtcms.com/a/122620.html

相关文章:

  • 大数据学习(104)-clickhouse与hdfs
  • Python web程序在服务器上面部署详细步骤
  • Java延迟队列
  • 铼赛智能Edge mini斩获2025法国设计大奖 | 重新定义数字化齿科美学
  • 深入解析 C++ 设计模式:原理、实现与应用
  • YOLOv12即插即用--CPAM
  • 【Kafka基础】消费者命令行完全指南:从基础到高级消费
  • 软考高级-系统架构设计师 案例题-软件架构设计
  • vue:前端预览 / chrome浏览器设置 / <iframe> 方法预览 doc、pdf / vue-pdf 预览pdf
  • 蓝桥杯 一年中的第几天(日期问题)
  • 如何运用浏览器进行各种调试?(网络、内存、控制台等调试用法)
  • 前端实战:基于Vue3与免费满血版DeepSeek实现无限滚动+懒加载+瀑布流模块及优化策略
  • Vert.x vs. Micronaut:2025年高并发Java框架选型指南
  • redisson常用加锁方式
  • 【代码模板】判断C语言中文件是否存在?错误:‘F_OK’未声明如何处理?(access;#include “unistd.h“)
  • 【智慧养猪场】-猪的行为分析视频数据集及展示(已做好分类)
  • C —— 宏
  • Redis-场景缓存+秒杀+管道+消息队列
  • 保留格式地一键翻译英文ppt
  • etf可以T+0交易吗?
  • 基础知识补充篇:什么是DAPP前端连接中的provider
  • 用网页JS实现数据添加和取出的操作,链表
  • Class 文件和类加载机制
  • 【10】数据结构的矩阵与广义表篇章
  • 聊透多线程编程-线程基础-3.C# Thread 如何从非UI线程直接更新UI元素
  • 学习MySQL的第六天
  • vue+uniapp 获取上一页直接传递的参数
  • 大数据(6)【Kettle入门指南】从零开始掌握ETL工具:基础操作与实战案例解析
  • Spring Boot 自定义配置类(包含字符串、数字、布尔、小数、集合、映射、嵌套对象)实现步骤及示例
  • PHP 表单处理详解