目录
一、场景
- 脱离场景谈设计模式毫无意义。先有某种场景,然后大神们(GoF)对其进行建模,以实现高内聚低耦合。
1、文本编辑器并不是一个好的例子,设备控制器才是
- 看了不少讲命令模式的文章,举的例子基本都是文本编辑器。
- 然而,并没有揭示命令模式的精髓。反而让读者觉得命令模式像过度设计。
- 生活中,我们手机上会装一个设备控制器 ,里面既可以控制空调的开关,还可以控制电视的开关。我将通过这个例子来阐述命令模式到底解决了什么问题。
2、设备控制器的demo
- 用户选择空调,并选择关闭:空调便关闭了。
- 用户选择电视机,并选择关闭:电视机便关闭了。
二、不用命令模式
1、代码
java
复制代码
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("电视已关闭");
}
}
java
复制代码
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、代码
java
复制代码
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("电视已关闭");
}
}
java
复制代码
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();
}
}
java
复制代码
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();
}
}
java
复制代码
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 新增代码
java
复制代码
public class StandByCommand extends Command {
@Override
public void execute() {
receiver.standby();
}
}
java
复制代码
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("洗衣机已待机");
}
}
java
复制代码
public class AirConditioner implements Receiver {
...
@Override
public void standby() {
System.out.println("空调已待机");
}
}
public class Television implements Receiver {
...
@Override
public void standby() {
System.out.println("电视已待机");
}
}
java
复制代码
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的建模可以吗?
java
复制代码
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、命令模式的价值
- 当对真实场景建模后,各部分的交互逻辑如下图所示,便可以采用命令模式实现"高内聚、低耦合"的代码设计。