设计模式系列—命令模式

《Head First设计模式》读书笔记

命令模式(封装调用)

一,场景介绍

1,需求

设计一个家电自动化遥控器的API,这个遥控器有7个可编程的插槽,每个插槽都有对应的开和关按钮,这个遥控器还有一个整体的撤销按钮。
  提供的家电厂商类有很多:Light、Door、TV等

2,思考

  • 对于遥控器,它只是知道按下按钮,然后执行对应的ON或者OFF命令,但是他不知道电器执行的具体细节。
  • 如果采用判断语句if,else来逐个判断,这样必然违背了设计的基本思想。
  • 命令模式刚好符合这个场景,命令模式可以将动作请求(命令)从对象执行对应的操作完全解耦出来,那么学习和理解命令模式就是接下来的重点。

二,介绍命令模式

1,餐厅订餐来理解命令模式

客户(Client)点餐,然后服务员(Invoker)将点餐内容记录下来形成一个订单(Command),然后服务员将订单交给厨房(Receiver),然后厨房做出餐点。首先需要理解的是,作为调度者的服务员,他只需要形成订单(也就是一个家电控制命令),然后将订单送出去,并不需要了解订单的内容是什么,也不需要由哪个厨师来完成,他只是简单的进行一个传递工作。这就是命令模式中一个重要的思想解耦。

2,命令模式中需要学习的重点

在命令模式中,我们不仅要学习命令模式的基本思想结构,我们还要学习,执行基本的单个命令模式,执行多组的命令模式,命令模式的撤销(回退到上一个状态)。最后要知道的就是命令模式的用途。

3,先实行一个基本的单个命令模式

先定义一个命令接口,可以让遥控器设置接口对象的时候都是统一的类型。

public interface Command {
    public void execute();
}

定义一个Light类,这个是目标,里面设置的有Light.on()方法。

public class Light {
    public Light(){}
    public void on(){
        System.out.println("Light is on!");
    }
}

设置一个开灯的命令来包装一下Light对象

public class LightOnCommand implements Command {
    private Light light;
    public LightOnCommand(Light light) {
        this.light = light;
    }
    @Override
    public void execute() {
        light.on();
    }
}

设置一个远程命令控制接口,注意思考为什么要设置真么一个控制接口

public class SimpleRemoteControl {
    private Command comd;
    public SimpleRemoteControl() {  
    }
    public void setCommand(Command command){
        this.comd = command;
    }
    public void buttonWaspressed(){
        comd.execute();
    }
}

最后一个测试类

public class Test {
    public static void main(String[] args) {
        SimpleRemoteControl control = new SimpleRemoteControl();
        Light light = new Light();
        LightOnCommand lightCommand = new LightOnCommand(light);
        control.setCommand(lightCommand);
        control.buttonWaspressed();
    }
}

三,命令模式的结构图

如下:

设计模式系列—命令模式_第1张图片
Paste_Image.png

这个结构图里面多了一个LightOffCommand,其添加方式和上面的简单设计模式实现代码是一样的,而且你还可以添加更多的TVCommand、DoorCommand等,这个可以自己实现。其实到这里我们已经掌握了命令设计模式了,下面只是对命令模式的一些扩展用法介绍,便于在实际开发中灵活扩展。

四,命令的撤回

命令撤回,关于撤回,我们可以联想一下我们日常中Ctrl+Z操作,我们很容易想到将命令放到一个堆栈里面,我们只需要挨个弹出来即可实现。
  当然,我们仍然可以按照书中的案例来熟悉一下命令模式。

1,案例介绍

比如吊扇(CeilingFan)他有高速、中速、低速、和关闭状态。沃恩需要记住电扇之前的运行状态,可能是上面四种的任意一个,比如低速,现在我们执行电扇,启动高速,撤回(undo)的时候就变为低速。下面请看代码

2,还是一样,先创建一个Command接口

public interface Command {
    public void execute();
    public void undo();
}

3,创建一个CeilingFan类

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;
int speed;

public CeilingFan() {
    speed = OFF;
}
public void high(){
    speed = HIGH;
    System.out.println("The speed of the ceilingFan is High");
}
public void medium(){
    speed = MEDIUM;
    System.out.println("The speed of the ceilingFan is medium");
}
public void low(){
    speed = LOW;
    System.out.println("The speed of the ceilingFan is low");
}
public void off(){
    System.out.println("The speed of the ceilingFan is off");
    speed = OFF;
}
public int getSpeed(){
    return speed;
}
}

4,创建一个CeilingFanHighCommand,当然你可以加上MediumCommand、LowCommand。其中undo就是一个撤回命令。

public class CeilingFanHighCommand implements Command{
private CeilingFan ceilingFan;
private int preSpeed;

public CeilingFanHighCommand(CeilingFan ceilingFan) {
    this.ceilingFan = ceilingFan;
}

@Override
public void execute() {
    preSpeed = ceilingFan.getSpeed();
    ceilingFan.high();
}

@Override
public void undo() {
    if(preSpeed == CeilingFan.HIGH){
        ceilingFan.high();
    }else if(preSpeed == CeilingFan.MEDIUM){
        ceilingFan.medium();
    }else if(preSpeed == CeilingFan.LOW){
        ceilingFan.low();
    }else if(preSpeed == CeilingFan.OFF){
        ceilingFan.off();
    }
}   
}

5,创建RemoteControl类

public class RemoteControl implements Command{
Command comd;
public RemoteControl() {}
public void setCommand(Command command){
    this.comd = command;
}
@Override
public void execute() {
    comd.execute();
}

@Override
public void undo() {
    comd.undo();
}
}

6,创建测试类

public class Test {
public static void main(String[] args) {
    RemoteControl control = new RemoteControl();
    CeilingFan ceilingFan = new CeilingFan();
    CeilingFanHighCommand ceilingFanHighCommand = new CeilingFanHighCommand(ceilingFan);
    control.setCommand(ceilingFanHighCommand);
    control.execute();
    control.undo();
}
}

五,运行一组命令

有时候,在特使情景的时候,我们可能需要运行一组命令,比如我们回家,我们想一键完成开门,开灯,然后打开电视等着看剧。或者更多复杂命令的组合要求一键同时执行。下面只实现同时开门,开灯,开电视。
  不多说,直接上代码:

1,Command接口统一类型

public interface Command {
public void execute();
}

2,几个实体类

public class Light {
public void on(){
    System.out.println("Light is on!");
}
}

public class Door {
public void open(){
    System.out.println("The door is openning!");
}
}

public class TV {
public void on(){
    System.out.println("the TV is on!");
}
}

3,几个实体类对应的打开命令类

public class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
    this.light = light;
}
@Override
public void execute() {
    light.on();
}
}

public class DoorOpenCommand implements Command{
private Door door;
public DoorOpenCommand(Door door) {
    this.door = door;
}
@Override
public void execute() {
    door.open();
}
}

public class TVOnCommand implements Command{
private TV tv;
public TVOnCommand(TV tv) {
    this.tv = tv;
}
@Override
public void execute() {
    tv.on();
}
}

4,新添加一个AllOnCommand类,注意此处,本来可以硬编码进去,但是为什么没有,体会设计模式的基本原则。

public class AllOnCommand implements Command{
private Command[] command;
public AllOnCommand(Command[] command) {
    this.command = command;
}
@Override
public void execute() {
    for(Command command:command){
        command.execute();
    }
}
}

5,测试类

public class Test {
public static void main(String[] args) {
    SimpleRemoteControl control = new SimpleRemoteControl();
    Light light = new Light();
    Door door = new Door();
    TV tv = new TV();
    LightOnCommand lightCommand = new LightOnCommand(light);
    DoorOpenCommand doorOpenCommand =new DoorOpenCommand(door);
    TVOnCommand tvCommand = new TVOnCommand(tv);
    Command[] commands ={lightCommand,doorOpenCommand,tvCommand};
    AllOnCommand allCommand = new AllOnCommand(commands);
    control.setCommand(allCommand);
    control.buttonWaspressed();
}
}

六,总结

命令模式主要就是要体会封装调用的思想,在实际开发中,消息队列是一个很常用的一个机制,他们不管队列里面是执行什么任务,网络请求呐或者数据运算呐,完全不用管具体业务,只需要执行命令就可以。比如会用在数据库日志中,通过记录命令来达到日志的各种功能,比如数据库恢复。等等。

你可能感兴趣的:(设计模式系列—命令模式)