"HeadFirst design mode" command mode Chapter VI - reading notes

"HeadFirst design mode" command mode Chapter VI - reading notes

Case Code link: https: //github.com/rainweb521/My-tutorial/tree/master/Design_patterns

1. Background

1.1 to analyze the dining room

From the restaurant to begin with, in order to explain the process command mode.

Orders encapsulates the request to prepare meals

Waitress taking orders and then call orders orderUp () method

Chefs prepare meals

In this way, the customer and waitress are decoupled, one day, different customers have different orders, the waitress know that all orders are supported orderUp () method, each call to this method it wants.

Waitresses and cooks are also decoupled orders encapsulates the details of the meal, she orders only call each method can, but also do cook meals based on corresponding orders. No direct communication between them.

image-20191023073214000

1.2 back to the remote control

This chapter needs to implement a remote control on the remote control only seven slots, and vendors have a lot of class, and later will add, so it is necessary to extract the feature classes in common, then you use the command mode , the "action requester" decoupling from "doer of the action" object.

Using a command object, the request (e.g., turn on the lights) encapsulated in a particular object (e.g., a living room lamp objects). So, if a command object is stored for each button, then when the button is pressed, you can ask the command object to do related work. Remote control does not need to know what the job content, as long as there is a command and the correct object objects can communicate, get things done on it. Therefore, an object remote control and lamp decoupled.
Because it is such a decoupling between objects, to describe the actual work of this model will not be easy.
Using this model, we can create an API, these commands will load objects into the slot button, the remote controller to try to keep the code simple. And put in the package together with the command object and the appliance automated work the work object.

2. Start Design

2.1 The first command objects

Implement command interface, so that all of the command object contains a method to achieve the same interface.

public interface Command {
    public void execute();
}

Turn on the lights in order to achieve

public class LightOnCommand implements Command {
    Light light;
//    给构造器传入某个电灯,方便以后调用execute
    public LightOnCommand(Light light){
        this.light = light;
    }
//    调用接受对象的on方法
    public void execute() {
        light.on();
    }
}

Lamp class

public class Light {
        private String name;
    public Light(String living_room) {
        this.name = living_room;
    }
    public Light() {
    }
    public void on(){
        System.out.println("打开灯");
    }
    public void off(){
        System.out.println("关闭灯");
    }
}

2.2 Use the command object

Suppose there is only one remote control, only one button and the corresponding slots.

public class SimpleRemoteController {
//    插槽持有命令,而这个命令控制着一个装置
    Command slot;

    public SimpleRemoteController() { }

//    用来设置插槽控制的命令,如果想要改变命令,可以多次调用
    public void setCommand(Command command) {
        this.slot = command;
    }
//    按下按钮,方法就会被调用
    public void buttonWasPressed(){
        slot.execute();
    }
}

2.3 Test remote control

public class RemoteControllerTest {
    public static void main(String[] args) {
//        实例化遥控器
        SimpleRemoteController remote = new SimpleRemoteController();
//        创建一个电灯对象,此对象就是请求接受者
        Light light = new Light();
//        创建命令,并将接收者传给它
        LightOnCommand lightOn = new LightOnCommand(light);
//        将命令传给调用者
        remote.setCommand(lightOn);
//        模拟按下按钮
        remote.buttonWasPressed();
    }
}

3. Design Patterns

3.1 schema definition

命令模式:将请求封装成对象,以便使用不用的请求,队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。

现在,仔细看这个定义。我们知道一个命令对象通过在特定接收者上绑定一组动作来封装一个请求。要达到这一点,命令对象将动作和接收者包进对象中。这个对象只暴露出一个execute方法,当此方法被调用的时候,接收者就会进行这些动作。从外面来看,其他对象不知道究竟哪个接收者进行了哪些动作,只知道如果调用 execute方法,请求的目的就能达到。

3.2 类图

image-20191023224003672

4.开始实现

4.1 实现遥控器

public class RemoteControl {
    Command[] onCommands;
    Command[] offCommands;

    /**
     * 初始化这两个开关的数组
     */
    public RemoteControl(){
        onCommands = new Command[7];
        offCommands = new Command[7];

        Command noCommand = new NoCommand();
        for (int i = 0 ;i < 7;i++){
            onCommands[i] = noCommand;
            offCommands[i] = noCommand;
        }
    }

    /**
     * 有三个命令,分别是插槽位置,开的命令,关的命令
     * @param slot
     * @param onCommand
     * @param offCommand
     */
    public void setCommand(int slot ,Command onCommand,Command offCommand){
        onCommands[slot] = onCommand;
        offCommands[slot] = offCommand;
    }
    public void onButtonWasPushed(int slot){
        onCommands[slot].execute();
    }
    public void offButtonWasPushed(int slot){
        offCommands[slot].execute();
    }

    @Override
    public String toString() {
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("\n-------Remote Control ------\n");
        for (int i = 0 ;i < onCommands.length;i++){
            stringBuffer.append("[slot"+i+"]"+onCommands[i].getClass().getName()+"        "
            +offCommands[i].getClass().getName()+"\n");
        }
        return stringBuffer.toString();
    }
}
public class NoCommand implements Command {
    public void execute() {

    }
}

Nocommand对象是一个空对象( null object)的例子。当你不想返回一个有意义的对象时,空对象就很有用。客户也可以将处理nul的责任转移给空对象。举例来说,遥控器不可能一出厂就设置了有意义的命令对象,所以提供了 Nocommand对象作为代用品,当调用它的 execute0方法时,这种对象什么事情都不做。
在许多设计模式中,都会看到空对象的使用。甚至有些时候,空对象本身也被视为是种设计模式。

4.2实现命令

public class LightOffCommand implements Command {
    Light light;

    public LightOffCommand(Light light) {
        this.light = light;
    }

    public void execute() {
        light.off();
    }
}

4.3测试遥控器

public class RemoteLoader {
    public static void main(String[] args) {
        RemoteControl remoteControl = new RemoteControl();

        Light livingroomLight = new Light("Living Room");
        Light kitchenLight = new Light("Kitchen");
//        ceilingFan = new

        LightOnCommand LivingRoomlightOnCommand = new LightOnCommand(livingroomLight);
        LightOffCommand LivingRoomlightOffCommand = new LightOffCommand(livingroomLight);
        LightOnCommand KitchenlightOnCommand = new LightOnCommand(livingroomLight);
        LightOffCommand KitchenlightOffCommand = new LightOffCommand(kitchenLight);

//        添加命令
        remoteControl.setCommand(0,LivingRoomlightOnCommand,LivingRoomlightOffCommand);

//        打印遥控器里的信息
        System.out.println(remoteControl);

//        执行开关
        remoteControl.onButtonWasPushed(0);
        remoteControl.offButtonWasPushed(0);
    }

}

4.4 查看我们的类图

经过以上代码的实现,基本完成了所需要的功能,这个时候看一下代码的UML图,这样可以更好的理解代码之间的继承关系。

image-20191024065748724

5.添加撤销

好了,我们现在需要在遥控器上加上撤销的功能。这个功能使用起来就像是这样的:比方说客厅的电灯是关闭的,然后你按下遥控器上的开启按钮,自然电灯就被打开了。现在如果按下撤销按钮,那么上一个动作将被倒转,在这个例子里,电灯将被关闭。在进入更复杂的例子之前,先让撤销按钮能够处理电灯

在命令接口中加入undo方法

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

在打开灯的命令中,执行off操作

public class LightOnCommand implements Command {
    Light light;
//    给构造器传入某个电灯,方便以后调用execute
    public LightOnCommand(Light light){
        this.light = light;
    }
//    调用接受对象的on方法
    public void execute() {
        light.on();
    }

    public void undo() {
        light.off();
    }
}

在关闭灯的命令中,执行on方法

public class LightOffCommand implements Command {
    Light light;

    public LightOffCommand(Light light) {
        this.light = light;
    }

    public void execute() {
        light.off();
    }

    public void undo() {
        light.on();
    }
}
public class NoCommand implements Command {
    public void execute() {

    }

    public void undo() {

    }
}

对遥控器进行一些修改,每次按下按钮的时候,都记录下来按动的哪个,并且可以随时调用undo方法。

public class RemoteControl {
    Command[] onCommands;
    Command[] offCommands;

    Command undoCommand;
    /**
     * 初始化这两个开关的数组
     */
    public RemoteControl(){
        onCommands = new Command[7];
        offCommands = new Command[7];

        Command noCommand = new NoCommand();
        for (int i = 0 ;i < 7;i++){
            onCommands[i] = noCommand;
            offCommands[i] = noCommand;
        }
        undoCommand = noCommand;
    }

    /**
     * 有三个命令,分别是插槽位置,开的命令,关的命令
     * @param slot
     * @param onCommand
     * @param offCommand
     */
    public void setCommand(int slot ,Command onCommand,Command offCommand){
        onCommands[slot] = onCommand;
        offCommands[slot] = offCommand;
    }
    public void onButtonWasPushed(int slot){
        onCommands[slot].execute();
        undoCommand = onCommands[slot];
    }
    public void offButtonWasPushed(int slot){
        offCommands[slot].execute();
        undoCommand = offCommands[slot];
    }

    public void undoButtonWasPushhed(){
        undoCommand.undo();
    }


    @Override
    public String toString() {
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("\n-------Remote Control ------\n");
        for (int i = 0 ;i < onCommands.length;i++){
            stringBuffer.append("[slot"+i+"]"+onCommands[i].getClass().getName()+"        "
            +offCommands[i].getClass().getName()+"\n");
        }
        return stringBuffer.toString();
    }
}

书中还加入了,使用状态实现撤销,并实现了电扇撤销的功能,虽然比电灯要复杂一些,但总体思路相同。

6.命令模式的更多用途

6.1 队列请求

命令可以将运算块打包(一个接收者和一组动作), 然后将它传来传去,就像是一般的对象一样。现在即使在命令对象被创建许久之后,运算依然可以被调用。事实上,它甚至可以在不同的线程中被调用。我们可以利用这样的特性行生一些应用,例如:日程安排( Scheduler)、线程池、工作队列等。
想象有一个工作队列:你在某一端添加命令,然后另端则是线程。线程进行下面的动作:从队列中取出一个命令,调用它的 execute()方法,等待这个调用完成, 然后将此命令对象丢弃,再取出下一个命令

请注意,工作队列类和进行计算的对象之间完全是解耦的。此刻线程可能在进行财务运算,下一刻却在读取网络数据。工作队列对象不在平到底做些什么,它们只知道取出命令对象,然后调用其execute方法。类似地,它们只要是实现命令模式的对象,就可以放入队列里,当线程可用时,就调用此对象的 execute方法。

6.2 日志请求

Some applications require that all of our actions are logged and can be after a system crash, recalling previous state to restore these actions. By two new methods (store (), load), command mode can support this. In Java, we can use serialization (Serialization) to achieve the object of these methods, it is generally believed serialization best used only in the persistent objects (persistence) how to do it? When we execute a command, the history is stored on disk. Once the system crashes, we can reload the command objects, and in batches in turn calls execute (of these objects. This way logs for the remote control is no sense, however, there are many applications call the action of large data structures can not be quickly stored at each change occurs through the use of logging, we can all operations since the last checkpoint (checkpoint) recorded, if the system is out of state, from the checkpoint to start the application of these operations. For example, for spreadsheet application, we may want to achieve is the wrong way to restore the operating records of the spreadsheet in the log, rather than each time there is a change to a spreadsheet recording the entire spreadsheet. for more advanced applications, these tips It can be extended to a transaction (transaction) processing, that is, a whole group of all of the operation must be completed, or without any operation.

Guess you like

Origin www.cnblogs.com/rain1024/p/11783436.html