Java设计模式之命令模式

概念和作用

命令模式将请求封装为对象,使请求的发送者和接收者解耦。通过参数化请求、支持队列、日志、撤销等功能,增强系统的灵活性和扩展性。

场景

1.需要解耦请求发送者和接收者。

2.需要支持命令的排队、撤销/重做、日志记录。

3.需要动态指定请求或组合多个请求。

示例

复制代码
// 1.命令接口
interface Command {
    void execute();
    void undo(); // 可选撤销功能
}

// 2.接收者(真正执行操作的对象)
class PhoneInventory {
    private int stock = 10;
    
    public void buyPhones(int quantity) {
        stock += quantity;
        System.out.println("采购成功,当前库存:" + stock);
    }
    
    public void sellPhones(int quantity) {
        if(stock >= quantity) {
            stock -= quantity;
            System.out.println("销售成功,当前库存:" + stock);
        } else {
            System.out.println("库存不足,销售失败");
        }
    }
}

// 3.具体命令:购买
class BuyCommand implements Command {
    private PhoneInventory inventory;
    private int quantity;

    public BuyCommand(PhoneInventory inventory, int quantity) {
        this.inventory = inventory;
        this.quantity = quantity;
    }

    @Override
    public void execute() {
        inventory.buyPhones(quantity);
    }

    @Override
    public void undo() {
        inventory.sellPhones(quantity);
    }
}

// 3.具体命令:销售
class SellCommand implements Command {
    private PhoneInventory inventory;
    private int quantity;

    public SellCommand(PhoneInventory inventory, int quantity) {
        this.inventory = inventory;
        this.quantity = quantity;
    }

    @Override
    public void execute() {
        inventory.sellPhones(quantity);
    }

    @Override
    public void undo() {
        inventory.buyPhones(quantity);
    }
}

// 4.调用者(触发命令的对象)
class OrderProcessor {
    private List<Command> commands = new ArrayList<>();

    // 添加命令
    public void addCommand(Command command) {
        commands.add(command);
    }

    // 执行命令
    public void processOrders() {
        for(Command cmd : commands) {
            cmd.execute();
        }
        commands.clear();
    }
}

// 5.客户端使用
public class Main {
    public static void main(String[] args) {
        // 接收者
        PhoneInventory inventory = new PhoneInventory();
        // 调用者
        OrderProcessor processor = new OrderProcessor();
        
        // 创建命令对象
        processor.addCommand(new BuyCommand(inventory, 5));
        processor.addCommand(new SellCommand(inventory, 3));
        
        // 统一执行命令
        processor.processOrders();
    }
}

优点和缺点

优点

**1.解耦:**调用者与具体操作解耦,调用者不需要知道具体实现。

**2.可扩展:**容易添加新命令,符合开闭原则。

**3.组合命令:**支持宏命令(组合多个命令)。

**4.支持撤销:**通过添加undo方法实现操作回滚。

缺点

**1.类膨胀:**每个命令都需要一个具体类。

**2.复杂度增加:**需要多层级对象协作。

不用命令模式的实现

复制代码
// 直接调用库存方法
public class WithoutCommandPattern {
    public static void main(String[] args) {
        PhoneInventory inventory = new PhoneInventory();
        
        // 直接调用具体方法
        inventory.buyPhones(5);
        inventory.sellPhones(3);
    }
}

缺陷

1.紧耦合:调用者直接依赖具体实现。

复制代码
// 不使用命令模式时
public class WithoutCommandPattern {
    public static void main(String[] args) {
        PhoneInventory inventory = new PhoneInventory();
        // 直接调用具体方法
        inventory.buyPhones(5);  // 直接依赖 buyPhones 方法
        inventory.sellPhones(3); // 直接依赖 sellPhones 方法
    }
}

问题说明:

调用者(客户端 main 方法)直接调用 PhoneInventory 的具体方法 buyPhonessellPhones。如果 PhoneInventory 的方法名或参数改变(如 buyPhones 改名为 purchasePhones),所有调用它的代码都需要修改。

2.难以扩展:新增功能需要修改现有代码。

假设需要新增一个 退货功能 returnPhones(int quantity),此时:

复制代码
// 非命令模式需要修改调用方
public class WithoutCommandPattern {
    public static void main(String[] args) {
        PhoneInventory inventory = new PhoneInventory();
        inventory.buyPhones(5);
        inventory.sellPhones(3);
        // 新增退货功能
        inventory.returnPhones(2); // 必须修改客户端代码
    }
}

问题说明:

调用者必须知道新方法 returnPhones 的存在并直接调用它。

违反开闭原则:扩展功能需要修改现有客户端代码。

对比命令模式:

命令模式只需新增 ReturnCommand 类,客户端通过添加命令对象即可扩展功能,无需修改现有调用逻辑。

3.无法支持撤销:需要额外维护操作历史。

在非命令模式中,若用户想要撤销一次销售操作:

复制代码
public class WithoutCommandPattern {
    public static void main(String[] args) {
        PhoneInventory inventory = new PhoneInventory();
        inventory.buyPhones(5);    // 初始库存 15
        inventory.sellPhones(3);    // 库存 12
        // 撤销销售操作(需要手动反向操作)
        inventory.buyPhones(3);     // 必须自行记录操作历史并反向调用
    }
}

问题说明:

调用者需自行记录每次操作的类型和参数(如销售了 3 台手机),并手动调用反向操作(如再采购 3 台)。

若操作历史复杂(如涉及多个对象),维护成本极高。

对比命令模式:

命令模式通过 Command 接口的 undo() 方法,自动记录操作历史并实现撤销。

4.缺乏灵活性:无法实现命令队列、延迟执行等高级功能。

假设需要将多个操作批量执行或延迟到特定时间执行:

复制代码
public class WithoutCommandPattern {
    public static void main(String[] args) {
        PhoneInventory inventory = new PhoneInventory();
        // 直接调用方法,无法延迟执行或批量管理
        inventory.buyPhones(5);
        inventory.sellPhones(3);
        // 若想实现延迟执行,需自行设计线程或定时任务
    }
}

问题说明:

调用者直接触发方法调用,所有操作立即执行。

若想实现命令队列、延迟执行或事务管理(如批量操作全部成功或全部失败),需额外开发复杂的逻辑。

对比命令模式:

命令模式通过 OrderProcessor 类管理命令队列,可轻松实现批量执行、延迟执行或事务回滚:

复制代码
// 命令模式支持延迟执行
processor.addCommand(new BuyCommand(inventory, 5));
processor.addCommand(new SellCommand(inventory, 3));
// 在需要时统一执行
processor.processOrders();

总结对比

|------|---------------------------|---------------------|
| 特性 | 非命令模式实现 | 命令模式实现 |
| 耦合性 | 调用者直接依赖 PhoneInventory 方法 | 调用者依赖抽象的 Command 接口 |
| 扩展性 | 需修改调用方代码 | 通过新增 Command 子类扩展 |
| 撤销支持 | 需自行维护操作历史 | 内置 undo() 方法实现撤销 |
| 灵活性 | 无法实现队列、延迟执行等高级功能 | 通过命令队列支持批量处理、延迟执行 |

实际意义

命令模式通过 封装请求为对象,将操作的具体实现与调用者解耦,从而解决了上述问题。对于需要支持复杂操作管理(如撤销、重做、事务)的场景,命令模式是更优的设计选择。而对于简单的一次性操作,直接调用可能更高效。

相关推荐
YoseZang19 分钟前
【设计模式】GoF设计模式之策略模式(Strategy Pattern)
设计模式·策略模式
世纪摆渡人22 分钟前
设计模式-策略模式(Strategy Pattern)
设计模式·策略模式
可儿·四系桜1 小时前
WebSocket:实时通信的新时代
java·网络·websocket·网络协议
forestsea1 小时前
Maven 插件机制与生命周期管理
java·maven
七月在野,八月在宇,九月在户1 小时前
maven 依赖冲突异常分析
java·maven
金融数据出海1 小时前
黄金、碳排放期货市场API接口文档
java·开发语言·spring boot·后端·金融·区块链
胡斌附体1 小时前
微服务中 本地启动 springboot 无法找到nacos配置 启动报错
java·spring boot·微服务·yml·naocs yml
薯条不要番茄酱1 小时前
【JVM】从零开始深度解析JVM
java·jvm
夏季疯2 小时前
学习笔记:黑马程序员JavaWeb开发教程(2025.3.31)
java·笔记·学习
yangyang_z2 小时前
【C++设计模式之Observer观察者模式】
c++·观察者模式·设计模式