「頭からデザインモード」コマンドモード章VI - ノートを読んで

「頭からデザインモード」コマンドモード章VI - ノートを読んで

ケースコードリンクします。https://github.com/rainweb521/My-tutorial/tree/master/Design_patterns

1.背景

ダイニングルームを分析する1.1

プロセス・コマンド・モードを説明するために、そもそもレストランから。

注文は食事を準備するための要求をカプセル化します

ウェイトレスが注文を受けた後、orderUp()メソッドを呼び出して注文

シェフが食事を準備します

このように、顧客やウェイトレスが切り離され、1日、別の顧客は異なる順序を持って、ウェイトレスはすべての注文がorderUp()メソッド、それは望んでこのメソッドをコールするたびにサポートされていることを知っています。

ウェイトレスや料理人にも注文を切り離され、食事の内容をカプセル化し、彼女は唯一の各メソッドはできますが、また、受注対応に基づいて食事を作るん呼び出す命令します。それらの間には直接の通信はありません。

画像-20191023073214000

バックリモコンに1.2

この章では、リモートコントロールにのみ7スロットをリモートコントロールを実装する必要があり、ベンダーは、クラスの多くを持って、そして後に追加されますので、あなたがコマンドモードを使用する一般的にフィーチャクラスを抽出する必要がありますオブジェクト「アクションの行為者」から切り離し、「アクション要求者」。

コマンドオブジェクトを使用して、特定のオブジェクトにカプセル化された要求(例えば、ライトをオンにする)(例えば、リビングルームランプオブジェクト)。コマンドオブジェクトは、各ボタンに格納されている場合は、ボタンが押されたときに、そして、あなたは、関連する作業を行うために、コマンドオブジェクトを求めることができます。そこにコマンドで、通信することができ、正しいオブジェクトオブジェクトは、物事はそれを成し遂げるなどのリモートコントロールがいる限り、どのような仕事内容を知る必要はありません。したがって、オブジェクトのリモートコントロールとランプが切り離さ。
それは、オブジェクト間のこのようなデカップリングであるため、このモデルの実際の作業は容易ではないだろう説明します。
このモデルを使用して、我々はAPIを作成することができ、これらのコマンドは、コードをシンプルにしようとするスロットボタン、リモコンにオブジェクトをロードします。そして、コマンドオブジェクトとアプライアンスの自動化作業、作業オブジェクトと一緒にパッケージに入れました。

2. [スタート]デザイン

2.1最初のコマンドオブジェクト

コマンドオブジェクトのすべてが同じインターフェイスを達成するための方法を含むように、コマンド・インタフェースを実装します。

public interface Command {
    public void execute();
}

達成するためにライトをオンにします

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

ランプのクラス

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コマンドオブジェクトを使用します

唯一の遠隔制御、唯一のボタンと対応するスロットがあると仮定する。

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

    public SimpleRemoteController() { }

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

2.3テストリモコン

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.デザインパターン

3.1スキーマ定義

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

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

3.2 类图

画像-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图,这样可以更好的理解代码之间的继承关系。

画像-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 日志请求

一部のアプリケーションでは、私たちの行動のすべてが記録されていることを必要とし、これらのアクションを復元する前の状態をリコール、システムクラッシュの後にすることができます。2つの新しいメソッド(店舗()、ロード)することで、コマンドモードでは、これをサポートすることができます。Javaでは、私たちはこれらのメソッドの目的を達成するためにシリアライズ(直列化)を使用することができ、それは一般的に信じられているシリアル化が最善だけ永続オブジェクト(永続)どのようにそれを行うために使用されますか?我々は、コマンドを実行すると、歴史は、ディスク上に格納されています。システムがクラッシュしたら、コマンドオブジェクトをリロードすることができ、かつターンの呼び出しでバッチでこれらのオブジェクトの(実行されます。リモートコントロールのためのこの方法でログをも意味はありません、しかし、多くのアプリケーションは、大規模なデータ構造のアクションを呼び出しがありますすぐにそれぞれの変更で保存することはできませんが、最後のチェックポイント(チェックポイント)を記録するので、システムが状態の外にある場合、我々はすべての操作は、チェックポイントからこれらの操作のアプリケーションを起動することができ、ログを使用して行われます。たとえば、スプレッドシートアプリケーションのために、我々は達成したいことがあり、スプレッドシート全体を記録するスプレッドシートに変更があるログにスプレッドシートの操作記録を復元するために間違った方法ではなく、それぞれの時間である。より高度なアプリケーション、これらのヒントをこれはつまり、操作の全てのグループ全体が完了、または任意の操作なしでなければならない、トランザクション(トランザクション)の処理に拡張することができます。

おすすめ

転載: www.cnblogs.com/rain1024/p/11783436.html