对命令模式的理解

一、场景

  • 脱离场景谈设计模式毫无意义。先有某种场景,然后大神们(GoF)对其进行建模,以实现高内聚低耦合。

1、文本编辑器并不是一个好的例子,设备控制器才是

  • 看了不少讲命令模式的文章,举的例子基本都是文本编辑器。
  • 然而,并没有揭示命令模式的精髓。反而让读者觉得命令模式像过度设计。
  • 生活中,我们手机上会装一个设备控制器,里面既可以控制空调的开关,还可以控制电视的开关。我将通过这个例子来阐述命令模式到底解决了什么问题

2、设备控制器的demo

  • 用户选择空调,并选择关闭:空调便关闭了。
  • 用户选择电视机,并选择关闭:电视机便关闭了。

二、不用命令模式

1、代码

  • 设备:
public class AirConditioner {
    public void turnOn() {
        System.out.println("空调已开启");
    }

    public void turnOff() {
        System.out.println("空调已关闭");
    }
}

public class Television {
    public void turnOn() {
        System.out.println("电视已开启");
    }

    public void turnOff() {
        System.out.println("电视已关闭");
    }
}
  • 客户端:
public class Application {
    public static void main(String[] args) {
        AirConditioner airConditioner = new AirConditioner();
        Television television = new Television();

        Scanner scanner = new Scanner(System.in);
        while (true) {
            String device = scanner.next();
            if (device.equals("exit")) {
                break;
            }

            String command = scanner.next();
            if (device.equals("airConditioner")) {
                if (command.equals("turnOn")) {
                    airConditioner.turnOn();
                } else if (command.equals("turnOff")) {
                    airConditioner.turnOff();
                }
            } else if (device.equals("television")) {
                if (command.equals("turnOn")) {
                    television.turnOn();
                } else if (command.equals("turnOff")) {
                    television.turnOff();
                }
            }
        }
    }
}
  • 结果:
    在这里插入图片描述

2、问题

  • 随着发展,设备大概率不止空调和电视机,一旦增加新设备,客户端的代码就要有很大的改动。而且,每台设备能执行的命令也不止开启和关闭,一旦新增命令,又要修改客户端的代码。这都违背了“开闭原则”。
  • 另外,各种设备的代码杂糅在一起,违背了“单一职责原则”

三、使用命令模式

1、代码

  • 设备:
public interface Receiver {
    void turnOn();

    void turnOff();
}

public class AirConditioner implements Receiver {
    @Override
    public void turnOn() {
        System.out.println("空调已开启");
    }

    @Override
    public void turnOff() {
        System.out.println("空调已关闭");
    }
}

public class Television implements Receiver {
    @Override
    public void turnOn() {
        System.out.println("电视已开启");
    }

    @Override
    public void turnOff() {
        System.out.println("电视已关闭");
    }
}
  • 命令:
public abstract class Command {
    protected Receiver receiver;

    public void setReceiver(Receiver receiver) {
        this.receiver = receiver;
    }

    public abstract void execute();
}

public class TurnOffCommand extends Command {
    @Override
    public void execute() {
        receiver.turnOff();
    }
}

public class TurnOnCommand extends Command {
    @Override
    public void execute() {
        receiver.turnOn();
    }
}
  • 设备管理器:DeviceManager
public class DeviceManager {
    private final Map<String, Receiver> devices;
    private final Map<String, Command> commands;

    private DeviceManager() {
        devices = ImmutableMap.of(
                DeviceEnum.AIR_CONDITIONER.getValue(), new AirConditioner(),
                DeviceEnum.TELEVISION.getValue(), new Television()
        );

        commands = ImmutableMap.of(
                CommandEnum.TURN_ON.getValue(), new TurnOnCommand(),
                CommandEnum.TURN_OFF.getValue(), new TurnOffCommand()
        );
    }

    public static DeviceManager getSingleton() {
        return new DeviceManager();
    }

    public void execute(String device, String commandType) {
        Receiver receiver = devices.get(device);
        Command command = commands.get(commandType);

        command.setReceiver(receiver);
        command.execute();
    }
}
  • 客户端:
public class Application {
    public static void main(String[] args) {
        DeviceManager deviceManager = DeviceManager.getSingleton();

        Scanner scanner = new Scanner(System.in);
        while (true) {
            String device = scanner.next();
            if (device.equals("exit")) {
                break;
            }

            String command = scanner.next();
            deviceManager.execute(device, command);
        }

    }
}

2、当需求变化时

  • 新增一个设备:洗衣机
  • 新增一个命令:待机

2.1 新增代码

  • 新增命令:
public class StandByCommand extends Command {
    @Override
    public void execute() {
        receiver.standby();
    }
}
  • 新增设备:
public interface Receiver {
    ...

    void standby(); // 新增方法
}

// 新增设备
public class Washer implements Receiver {
    @Override
    public void turnOn() {
        System.out.println("洗衣机已开启");
    }

    @Override
    public void turnOff() {
        System.out.println("洗衣机已关闭");
    }

    @Override
    public void standby() {
        System.out.println("洗衣机已待机");
    }
}
  • 修改设备(新增方法):
public class AirConditioner implements Receiver {
    ...

    @Override
    public void standby() {
        System.out.println("空调已待机");
    }
}

public class Television implements Receiver {
    ...

    @Override
    public void standby() {
        System.out.println("电视已待机");
    }
}
  • 修改设备管理器:
public class DeviceManager {
    private final Map<String, Receiver> devices;
    private final Map<String, Command> commands;

    private DeviceManager() {
        devices = ImmutableMap.of(
                ...
                DeviceEnum.WASHER.getValue(), new Washer()
        );

        commands = ImmutableMap.of(
                ...
				CommandEnum.STAND_BY.getValue(), new StandByCommand()
        );
    }
	
	...
}

2.2 优点

  • 之前写的代码,一行都没改动。只是通过新增代码,便实现了需求:
    在这里插入图片描述
    • 尽可能符合“开闭原则”。
  • 各个设备、各个命令都很单一,尽可能符合“单一职责”。

四、进一步思考

1、省略对Command的建模可以吗?

  • 省略后的伪代码:
public class DeviceManager {
	...
	public void execute(String device, String commandType) {
		// 根据device找到对应的实体
	 	Device realDevice =	deviceMap.get(device);
		
		// 每个device根据commandType执行不同的逻辑
		realDevice.execute(commandType);
	}
	...
}

// 各个device都要实现这样的路由逻辑:
public void execute(String commandType) {
	if ("turnOn".equals(commandType)) {
		...
	} else if ("turnOff".equals(commandType)) {
		...
	} else {
		...
	}
}
  • 显然,去除对Command的建模后,代码变得冗余了。

2、命令模式的价值

  • 当对真实场景建模后,各部分的交互逻辑如下图所示,便可以采用命令模式实现“高内聚、低耦合”的代码设计。
    在这里插入图片描述

相关推荐

  1. 适配器模式理解

    2024-05-04 10:44:03       47 阅读
  2. 原型模式理解

    2024-05-04 10:44:03       47 阅读
  3. 外观模式理解

    2024-05-04 10:44:03       35 阅读
  4. 策略模式理解

    2024-05-04 10:44:03       28 阅读
  5. 建造者模式理解

    2024-05-04 10:44:03       36 阅读
  6. 观察者模式理解

    2024-05-04 10:44:03       33 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-05-04 10:44:03       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-05-04 10:44:03       106 阅读
  3. 在Django里面运行非项目文件

    2024-05-04 10:44:03       87 阅读
  4. Python语言-面向对象

    2024-05-04 10:44:03       96 阅读

热门阅读

  1. Python进阶之-ast使用详解

    2024-05-04 10:44:03       30 阅读
  2. 前端 TS

    2024-05-04 10:44:03       31 阅读
  3. 时间复杂度和空间复杂度

    2024-05-04 10:44:03       36 阅读
  4. C# do...while循环

    2024-05-04 10:44:03       34 阅读
  5. 富格林:了解黑幕套路正规方法预防

    2024-05-04 10:44:03       29 阅读
  6. 算法--回溯法

    2024-05-04 10:44:03       26 阅读
  7. 第十三节:Vben Admin实战-系统管理之菜单管理

    2024-05-04 10:44:03       35 阅读
  8. 动态sql

    动态sql

    2024-05-04 10:44:03      32 阅读
  9. vue2 + antvx6 实现流程图功能

    2024-05-04 10:44:03       26 阅读
  10. 如何判断嵌入式平台OpenCV在使用硬件编解码器?

    2024-05-04 10:44:03       23 阅读
  11. UNIAPP&小程序从入门到精通

    2024-05-04 10:44:03       25 阅读
  12. 15. 三数之和 - 力扣(LeetCode)

    2024-05-04 10:44:03       32 阅读
  13. 等保测评试题(一)

    2024-05-04 10:44:03       26 阅读