行为型-命令模式

1. 项目结构

bash 复制代码
	command-pattern-demo/
	├── pom.xml
	├── src/
	│   ├── main/
	│   │   ├── java/
	│   │   │   └── com/
	│   │   │       └── demo/
	│   │   │           └── command/
	│   │   │               ├── Command.java
	│   │   │               ├── Light.java
	│   │   │               ├── LightOnCommand.java
	│   │   │               ├── LightOffCommand.java
	│   │   │               ├── CeilingFan.java
	│   │   │               ├── CeilingFanHighCommand.java
	│   │   │               ├── CeilingFanOffCommand.java
	│   │   │               ├── Stereo.java
	│   │   │               ├── StereoOnWithCDCommand.java
	│   │   │               ├── StereoOffCommand.java
	│   │   │               ├── MacroCommand.java
	│   │   │               ├── RemoteControl.java
	│   │   │               ├── NoCommand.java
	│   │   │               └── Client.java
	│   │   └── resources/
	└── test/

Maven配置文件 (pom.xml)

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.demo</groupId>
    <artifactId>command-pattern-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2. 代码实现

  1. 命令接口 (Command.java)

    java 复制代码
    package com.demo.command;
    
    /**
     * 命令接口:所有命令都需要实现这个接口
     */
    @FunctionalInterface
    public interface Command {
        void execute();
        
        // Java 8 支持默认方法,可以添加默认实现
        default void undo() {
            System.out.println("撤销操作未实现");
        }
    }
  2. 接收者类 - 电灯 (Light.java)

    java 复制代码
    package com.demo.command;
    
    import lombok.Getter;
    
    /**
     * 接收者类:知道如何执行具体的操作
     */
    @Getter
    public class Light {
        private String location;
        private boolean isOn = false;
        
        public Light(String location) {
            this.location = location;
        }
        
        public void on() {
            this.isOn = true;
            System.out.println(location + " 电灯已打开");
        }
        
        public void off() {
            this.isOn = false;
            System.out.println(location + " 电灯已关闭");
        }
    }
  3. 具体命令类 - 打开电灯 (LightOnCommand.java)

    java 复制代码
    package com.demo.command;
    
    /**
     * 具体命令类:实现打开电灯的命令
     */
    public class LightOnCommand implements Command {
        private Light light;
        
        public LightOnCommand(Light light) {
            this.light = light;
        }
        
        @Override
        public void execute() {
            light.on();
        }
        
        @Override
        public void undo() {
            light.off();
        }
        
        @Override
        public String toString() {
            return "打开 " + light.getLocation() + " 的电灯";
        }
    }
  4. 具体命令类 - 关闭电灯 (LightOffCommand.java)

    java 复制代码
    package com.demo.command;
    
    /**
     * 具体命令类:实现关闭电灯的命令
     */
    public class LightOffCommand implements Command {
        private Light light;
        
        public LightOffCommand(Light light) {
            this.light = light;
        }
        
        @Override
        public void execute() {
            light.off();
        }
        
        @Override
        public void undo() {
            light.on();
        }
        
        @Override
        public String toString() {
            return "关闭 " + light.getLocation() + " 的电灯";
        }
    }
  5. 接收者类 - 吊扇 (CeilingFan.java)

    java 复制代码
    package com.demo.command;
    
    import lombok.Getter;
    
    /**
     * 接收者类:吊扇
     */
    @Getter
    public class CeilingFan {
        public static final int HIGH = 3;
        public static final int MEDIUM = 2;
        public static final int LOW = 1;
        public static final int OFF = 0;
        
        private String location;
        private int speed;
        
        public CeilingFan(String location) {
            this.location = location;
            this.speed = OFF;
        }
        
        public void high() {
            this.speed = HIGH;
            System.out.println(location + " 吊扇设置为高速");
        }
        
        public void medium() {
            this.speed = MEDIUM;
            System.out.println(location + " 吊扇设置为中速");
        }
        
        public void low() {
            this.speed = LOW;
            System.out.println(location + " 吊扇设置为低速");
        }
        
        public void off() {
            this.speed = OFF;
            System.out.println(location + " 吊扇已关闭");
        }
    }
  6. 具体命令类 - 吊扇高速命令 (CeilingFanHighCommand.java)

    java 复制代码
    package com.demo.command;
    
    /**
     * 具体命令类:设置吊扇为高速
     */
    public class CeilingFanHighCommand implements Command {
        private CeilingFan ceilingFan;
        private int previousSpeed;
        
        public CeilingFanHighCommand(CeilingFan ceilingFan) {
            this.ceilingFan = ceilingFan;
        }
        
        @Override
        public void execute() {
            previousSpeed = ceilingFan.getSpeed();
            ceilingFan.high();
        }
        
        @Override
        public void undo() {
            if (previousSpeed == CeilingFan.HIGH) {
                ceilingFan.high();
            } else if (previousSpeed == CeilingFan.MEDIUM) {
                ceilingFan.medium();
            } else if (previousSpeed == CeilingFan.LOW) {
                ceilingFan.low();
            } else {
                ceilingFan.off();
            }
        }
        
        @Override
        public String toString() {
            return "设置 " + ceilingFan.getLocation() + " 的吊扇为高速";
        }
    }
  7. 具体命令类 - 关闭吊扇命令 (CeilingFanOffCommand.java)

    java 复制代码
    package com.demo.command;
    
    /**
     * 具体命令类:关闭吊扇
     */
    public class CeilingFanOffCommand implements Command {
        private CeilingFan ceilingFan;
        private int previousSpeed;
        
        public CeilingFanOffCommand(CeilingFan ceilingFan) {
            this.ceilingFan = ceilingFan;
        }
        
        @Override
        public void execute() {
            previousSpeed = ceilingFan.getSpeed();
            ceilingFan.off();
        }
        
        @Override
        public void undo() {
            if (previousSpeed == CeilingFan.HIGH) {
                ceilingFan.high();
            } else if (previousSpeed == CeilingFan.MEDIUM) {
                ceilingFan.medium();
            } else if (previousSpeed == CeilingFan.LOW) {
                ceilingFan.low();
            } else {
                ceilingFan.off();
            }
        }
        
        @Override
        public String toString() {
            return "关闭 " + ceilingFan.getLocation() + " 的吊扇";
        }
    }
  8. 接收者类 - 音响 (Stereo.java)

    java 复制代码
    package com.demo.command;
    
    import lombok.Getter;
    
    /**
     * 接收者类:音响系统
     */
    @Getter
    public class Stereo {
        private String location;
        private int volume = 5;
        private boolean isOn = false;
        
        public Stereo(String location) {
            this.location = location;
        }
        
        public void on() {
            this.isOn = true;
            System.out.println(location + " 音响已打开");
        }
        
        public void off() {
            this.isOn = false;
            System.out.println(location + " 音响已关闭");
        }
        
        public void setCD() {
            System.out.println(location + " 音响设置为CD模式");
        }
        
        public void setVolume(int volume) {
            this.volume = volume;
            System.out.println(location + " 音响音量设置为: " + volume);
        }
    }
  9. 具体命令类 - 打开音响并播放CD (StereoOnWithCDCommand.java)

    java 复制代码
    package com.demo.command;
    
    /**
     * 具体命令类:打开音响并播放CD
     */
    public class StereoOnWithCDCommand implements Command {
        private Stereo stereo;
        
        public StereoOnWithCDCommand(Stereo stereo) {
            this.stereo = stereo;
        }
        
        @Override
        public void execute() {
            stereo.on();
            stereo.setCD();
            stereo.setVolume(11);
        }
        
        @Override
        public void undo() {
            stereo.off();
        }
        
        @Override
        public String toString() {
            return "打开 " + stereo.getLocation() + " 的音响并播放CD";
        }
    }
  10. 具体命令类 - 关闭音响 (StereoOffCommand.java)

    java 复制代码
    package com.demo.command;
    
    /**
     * 具体命令类:关闭音响
     */
    public class StereoOffCommand implements Command {
        private Stereo stereo;
        
        public StereoOffCommand(Stereo stereo) {
            this.stereo = stereo;
        }
        
        @Override
        public void execute() {
            stereo.off();
        }
        
        @Override
        public void undo() {
            stereo.on();
            stereo.setCD();
            stereo.setVolume(11);
        }
        
        @Override
        public String toString() {
            return "关闭 " + stereo.getLocation() + " 的音响";
        }
    }
  11. 宏命令类 - 一次执行多个命令 (MacroCommand.java)

    java 复制代码
    package com.demo.command;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * 宏命令:一次执行多个命令
     */
    public class MacroCommand implements Command {
        private List<Command> commands;
        
        public MacroCommand(List<Command> commands) {
            this.commands = new ArrayList<>(commands);
        }
        
        @Override
        public void execute() {
            System.out.println("=== 开始执行宏命令 ===");
            commands.forEach(Command::execute);
            System.out.println("=== 宏命令执行完成 ===");
        }
        
        @Override
        public void undo() {
            System.out.println("=== 开始撤销宏命令 ===");
            // 反向执行撤销操作
            for (int i = commands.size() - 1; i >= 0; i--) {
                commands.get(i).undo();
            }
            System.out.println("=== 宏命令撤销完成 ===");
        }
        
        public void addCommand(Command command) {
            commands.add(command);
        }
        
        @Override
        public String toString() {
            return "宏命令,包含 " + commands.size() + " 个子命令";
        }
    }
  12. 空命令类 - 默认空实现 (NoCommand.java)

    java 复制代码
    package com.demo.command;
    
    /**
     * 空命令:用于初始化遥控器按钮,避免空指针检查
     * 这是空对象模式的应用
     */
    public class NoCommand implements Command {
        @Override
        public void execute() {
            // 什么都不做
        }
        
        @Override
        public void undo() {
            // 什么都不做
        }
        
        @Override
        public String toString() {
            return "空命令";
        }
    }
  13. 调用者类 - 遥控器 (RemoteControl.java)

    java 复制代码
    package com.demo.command;
    
    import java.util.Stack;
    
    /**
     * 调用者类:遥控器,持有命令对象并触发命令执行
     */
    public class RemoteControl {
        private Command[] onCommands;
        private Command[] offCommands;
        private Stack<Command> undoStack;
        
        public RemoteControl() {
            onCommands = new Command[7];
            offCommands = new Command[7];
            undoStack = new Stack<>();
            
            Command noCommand = new NoCommand();
            for (int i = 0; i < 7; i++) {
                onCommands[i] = noCommand;
                offCommands[i] = noCommand;
            }
        }
        
        public void setCommand(int slot, Command onCommand, Command offCommand) {
            if (slot < 0 || slot >= onCommands.length) {
                throw new IllegalArgumentException("无效的插槽: " + slot);
            }
            
            onCommands[slot] = onCommand;
            offCommands[slot] = offCommand;
        }
        
        public void onButtonWasPushed(int slot) {
            if (slot < 0 || slot >= onCommands.length) {
                throw new IllegalArgumentException("无效的插槽: " + slot);
            }
            
            onCommands[slot].execute();
            undoStack.push(onCommands[slot]);
        }
        
        public void offButtonWasPushed(int slot) {
            if (slot < 0 || slot >= offCommands.length) {
                throw new IllegalArgumentException("无效的插槽: " + slot);
            }
            
            offCommands[slot].execute();
            undoStack.push(offCommands[slot]);
        }
        
        public void undoButtonWasPushed() {
            if (!undoStack.isEmpty()) {
                Command lastCommand = undoStack.pop();
                System.out.println("=== 执行撤销操作 ===");
                lastCommand.undo();
            } else {
                System.out.println("没有可撤销的操作");
            }
        }
        
        public void showCommands() {
            System.out.println("\n======= 遥控器配置 =======");
            for (int i = 0; i < onCommands.length; i++) {
                System.out.println("[插槽 " + i + "] " + 
                        onCommands[i] + " / " + offCommands[i]);
            }
            System.out.println("=========================\n");
        }
        
        // 使用Java 8的Lambda表达式创建命令(函数式接口)
        public void setLambdaCommand(int slot, Runnable onAction, Runnable offAction) {
            setCommand(slot, 
                    () -> onAction.run(), 
                    () -> offAction.run());
        }
    }
  14. 客户端类 - 演示使用 (Client.java)

    java 复制代码
    package com.demo.command;
    
    import java.util.Arrays;
    
    /**
     * 客户端:演示命令模式的使用
     */
    public class Client {
        public static void main(String[] args) {
            System.out.println("=== 命令模式演示:智能家居控制系统 ===\n");
            
            // 1. 创建接收者(设备)
            Light livingRoomLight = new Light("客厅");
            Light kitchenLight = new Light("厨房");
            CeilingFan ceilingFan = new CeilingFan("卧室");
            Stereo stereo = new Stereo("客厅");
            
            // 2. 创建具体命令
            Command livingRoomLightOn = new LightOnCommand(livingRoomLight);
            Command livingRoomLightOff = new LightOffCommand(livingRoomLight);
            Command kitchenLightOn = new LightOnCommand(kitchenLight);
            Command kitchenLightOff = new LightOffCommand(kitchenLight);
            Command ceilingFanHigh = new CeilingFanHighCommand(ceilingFan);
            Command ceilingFanOff = new CeilingFanOffCommand(ceilingFan);
            Command stereoOnWithCD = new StereoOnWithCDCommand(stereo);
            Command stereoOff = new StereoOffCommand(stereo);
            
            // 3. 创建调用者(遥控器)
            RemoteControl remoteControl = new RemoteControl();
            
            // 4. 设置遥控器按钮
            remoteControl.setCommand(0, livingRoomLightOn, livingRoomLightOff);
            remoteControl.setCommand(1, kitchenLightOn, kitchenLightOff);
            remoteControl.setCommand(2, ceilingFanHigh, ceilingFanOff);
            remoteControl.setCommand(3, stereoOnWithCD, stereoOff);
            
            // 5. 显示遥控器配置
            remoteControl.showCommands();
            
            // 6. 测试遥控器功能
            System.out.println("=== 测试遥控器功能 ===");
            remoteControl.onButtonWasPushed(0);  // 打开客厅灯
            remoteControl.offButtonWasPushed(0); // 关闭客厅灯
            remoteControl.undoButtonWasPushed(); // 撤销
            
            remoteControl.onButtonWasPushed(1);  // 打开厨房灯
            remoteControl.onButtonWasPushed(2);  // 打开吊扇高速
            remoteControl.onButtonWasPushed(3);  // 打开音响
            
            System.out.println("\n=== 测试撤销功能 ===");
            remoteControl.undoButtonWasPushed(); // 撤销音响
            remoteControl.undoButtonWasPushed(); // 撤销吊扇
            remoteControl.undoButtonWasPushed(); // 撤销厨房灯
            
            // 7. 创建宏命令
            System.out.println("\n=== 测试宏命令 ===");
            Command partyOnMacro = new MacroCommand(
                    Arrays.asList(livingRoomLightOn, stereoOnWithCD, ceilingFanHigh)
            );
            Command partyOffMacro = new MacroCommand(
                    Arrays.asList(livingRoomLightOff, stereoOff, ceilingFanOff)
            );
            
            remoteControl.setCommand(4, partyOnMacro, partyOffMacro);
            remoteControl.showCommands();
            
            System.out.println("=== 执行派对模式 ===");
            remoteControl.onButtonWasPushed(4);  // 执行宏命令
            System.out.println("\n=== 结束派对模式 ===");
            remoteControl.offButtonWasPushed(4); // 执行关闭宏命令
            remoteControl.undoButtonWasPushed(); // 撤销
            
            // 8. 使用Java 8 Lambda表达式(函数式接口)
            System.out.println("\n=== 使用Lambda表达式 ===");
            Light garageLight = new Light("车库");
            
            // 使用Lambda表达式创建命令
            remoteControl.setLambdaCommand(5, 
                    garageLight::on,  // 方法引用
                    garageLight::off  // 方法引用
            );
            
            remoteControl.onButtonWasPushed(5);
            remoteControl.offButtonWasPushed(5);
            
            // 9. 演示空命令模式
            System.out.println("\n=== 测试空命令 ===");
            remoteControl.onButtonWasPushed(6);  // 未设置的插槽,使用空命令
            remoteControl.undoButtonWasPushed(); // 没有可撤销的操作
        }
    }
  15. 扩展示例:使用命令队列

    java 复制代码
    package com.demo.command;
    
    import java.util.concurrent.BlockingQueue;
    import java.util.concurrent.LinkedBlockingQueue;
    
    /**
     * 命令队列:支持异步执行命令
     */
    public class CommandQueue {
        private BlockingQueue<Command> queue;
        private volatile boolean running = true;
        
        public CommandQueue() {
            queue = new LinkedBlockingQueue<>();
            startWorker();
        }
        
        private void startWorker() {
            Thread worker = new Thread(() -> {
                while (running) {
                    try {
                        Command command = queue.take();
                        System.out.println("[队列执行] 开始执行命令");
                        command.execute();
                        System.out.println("[队列执行] 命令执行完成");
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                }
            });
            worker.setDaemon(true);
            worker.start();
        }
        
        public void addCommand(Command command) {
            queue.offer(command);
        }
        
        public void shutdown() {
            running = false;
        }
    }

3. 构建和运行

  1. 使用Maven编译项目:

    bash 复制代码
    mvn clean compile
  2. 运行主程序:

    bash 复制代码
    mvn exec:java -Dexec.mainClass="com.demo.command.Client"
  3. 直接运行:

    bash 复制代码
    java -cp target/classes com.demo.command.Client

4. 核心概念

  • 命令模式将"请求"封装成对象,使得可以参数化其他对象,并支持请求排队、记录日志、撤销等操作。

  • 主要角色:

    • Command(命令接口):定义执行操作的接口

    • ConcreteCommand(具体命令):实现命令接口,绑定接收者和动作

    • Receiver(接收者):知道如何执行具体的操作

    • Invoker(调用者):持有命令对象并触发命令执行

    • Client(客户端):创建具体命令并设置接收者

  • 优点:

    • 解耦调用者和接收者

    • 支持撤销和重做

    • 支持命令队列和日志

    • 容易扩展新的命令

相关推荐
梵豪2 天前
使用命令模式实现《植物大战僵尸》兵营生产系统
命令模式
stevenzqzq4 天前
Compose重组的概念1
命令模式·compose
老朱佩琪!5 天前
Unity命令模式
unity·游戏引擎·命令模式
阿拉斯攀登5 天前
设计模式:Spring MVC 中命令模式的核心映射与设计逻辑
spring·设计模式·mvc·命令模式
阿拉斯攀登8 天前
设计模式:命令模式
设计模式·命令模式
阿拉斯攀登8 天前
设计模式:命令模式(Spring MVC中的实践)
设计模式·springmvc·命令模式
syt_10139 天前
设计模式之-命令模式
设计模式·命令模式
Poetinthedusk10 天前
设计模式-命令模式
windows·设计模式·c#·wpf·命令模式
刺客xs11 天前
Qt------信号槽,属性,对象树
开发语言·qt·命令模式