对命令模式的理解

发布于:2024-05-04 ⋅ 阅读:(41) ⋅ 点赞:(0)

一、场景

  • 脱离场景谈设计模式毫无意义。先有某种场景,然后大神们(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、命令模式的价值

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

网站公告

今日签到

点亮在社区的每一天
去签到