デザインモード-コマンドモード

1はじめに

ソフトウェア開発システムでは、「メソッドリクエスター」と「メソッド実装者」の間に密接な関係があり、ソフトウェア機能の拡張や保守に役立たないことがよくあります。たとえば、メソッドを「元に戻す、やり直す、記録する」のは不便なので、「メソッドのリクエスターと実装者をどのように切り離すか」が非常に重要になり、コマンドモードでこれをうまく解決できます。

実生活では、コマンドパターンの例がたくさんあります。たとえば、テレビを見ているときは、リモコンを押すだけでチャンネル切り替えが完了します。これは、チャンネル切り替え要求とチャンネル切り替え処理を完全に切り離すコマンドモードです。テレビのリモコン(コマンド送信者)は、ボタン(特定のコマンド)を使用してテレビ(コマンド受信機)をリモート制御します。

別の例として、レストランに行って食事をする場合、メニューはゲストが到着するまでカスタマイズされませんが、事前に構成されています。このように、ゲストは一時的な注文を許可するのではなく、来たときに料理を注文するだけで済みます。レストランが提供するメニューは、コマンドモードの実施形態であるリクエストと処理を分離することと同等です。

2.コマンドモードの定義と特徴

コマンドモードは次のように定義されます。リクエストをオブジェクトとしてカプセル化し、リクエストを発行する責任とリクエストを実行する責任を分離します。このように、2つはコマンドオブジェクトを介して通信するため、コマンドオブジェクトの保存、転送、呼び出し、追加、および管理に便利です。

コマンドモードの主な利点は次のとおりです。

  1. ミドルウェア(抽象インターフェース)を導入することにより、システムの結合度を減らします。
  2. 拡張性は良好で、コマンドの追加や削除は非常に便利です。コマンドモードを使用してコマンドを追加および削除しても、他のカテゴリには影響せず、「開閉の原則」を満たします。
  3. マクロコマンドを実現できます。コマンドモードを結合モードと組み合わせて、複数のコマンドを結合コマンド、つまりマクロコマンドにアセンブルすることができます。
  4. 元に戻す操作とやり直し操作を実現するのに便利です。コマンドモードと後述のメモモードを組み合わせることで、コマンドのキャンセルや復元を実現できます。
  5. 既存のコマンドに基づいて、追加の機能を追加できます。たとえば、デコレータモードと組み合わせたロギングはより柔軟になります。

欠点は次のとおりです。

  1. 多数の特定のコマンドクラスが生成される場合があります。特定の操作ごとに特定のコマンドクラスを設計する必要があるため、システムの複雑さが増します。
  2. コマンドモードの結果は、実際にはレシーバーの実行結果ですが、コマンドの形式でリクエストと実装を構造化、分離するために、追加のタイプ構造が導入されています(リクエスターと抽象コマンドインターフェイスが導入されています) 、これは理解の難しさを増します。しかし、これはデザインパターンの一般的な問題でもあります。抽象化によってクラスの数が必然的に増加します。コードの抽出は、コードの集約よりも確実に理解するのが困難です。

3.コマンドモードの構造と実現

システム内の関連する操作をコマンドに抽象化して、呼び出し元を実装者から分離することができます。構造は次のとおりです。

1.モデルの構造

コマンドモードには、次の主な役割が含まれます。

  1. 抽象コマンドクラス(Command)の役割:コマンドを実行するためのインターフェイスを宣言し、コマンドを実行するための抽象メソッドexecute()を持っています。
  2. 具象コマンドクラス(具象コマンド)の役割:抽象コマンドクラスの具象実現クラスであり、レシーバーオブジェクトを所有し、レシーバーの関数を呼び出してコマンドで実行する操作を完了します。
  3. 実装者/受信者(受信者)の役割:コマンド機能の関連操作を実行し、特定のコマンドオブジェクトビジネスの実際の実装者です。
  4. 呼び出し元/要求者(呼び出し元)の役割:要求の送信者であり、通常は多数のコマンドオブジェクトがあり、コマンドオブジェクトにアクセスして関連する要求を実行しますが、受信者には直接アクセスしません。

ここに画像の説明を挿入します

2.パターンの実装

package command;

public class CommandPattern {
    
    
    public static void main(String[] args) {
    
    
        Command cmd = new ConcreteCommand();
        Invoker ir = new Invoker(cmd);
        System.out.println("客户访问调用者的call()方法...");
        ir.call();
    }
}

//调用者
class Invoker {
    
    
    private Command command;

    public Invoker(Command command) {
    
    
        this.command = command;
    }

    public void setCommand(Command command) {
    
    
        this.command = command;
    }

    public void call() {
    
    
        System.out.println("调用者执行命令command...");
        command.execute();
    }
}

//抽象命令
interface Command {
    
    
    public abstract void execute();
}

//具体命令
class ConcreteCommand implements Command {
    
    
    private Receiver receiver;

    ConcreteCommand() {
    
    
        receiver = new Receiver();
    }

    public void execute() {
    
    
        receiver.action();
    }
}

//接收者
class Receiver {
    
    
    public void action() {
    
    
        System.out.println("接收者的action()方法被调用...");
    }
}

顧客が呼び出し元のcall()メソッドにアクセスします...
呼び出し元がコマンドコマンドを実行します...
受信者のaction()メソッドが呼び出されます...

4.コマンドモードの適用例

[例1]コマンドモードを使用して、顧客が朝食のためにレストランに行く例を実現する

分析:お客様はレストランに行くときに朝食にビーフン、フォー、ワンタンを選ぶことができます。お客様はウェイターから上記の朝食のいくつかを選ぶことができ、ウェイターは関係するシェフにそれを作るように顧客の要求をします。ここで、朝食の注文は「コマンド」、ウェイターは「発信者」、シェフは「レシーバー」に相当するため、コマンドモードを使用する方が適切です。

  • まず、抽象コマンドクラスである朝食クラス(Breakfast)を、何をすべきかを示す抽象メソッドcooking()で定義します。
  • 次に、そのサブクラスChangFen、HunTun、およびHeFenを定義します。これらは朝食クラスのcooking()メソッドを実装する特定のコマンドクラスですが、特定ではありませんが、引き渡されます。特定のシェフに対して実行してください。
  • 特定のシェフには、ChangFenChef、HunTunChef、HeFenChefが含まれ、注文の受信者です。

この例では、シェフの料理の効果を示したいので、各シェフクラスをJFrameのサブクラスとして定義します。最後に、顧客から料理のリクエストを受け取り、料理コマンドを送信するウェイタークラス(Waiter)を定義します。顧客クラスはウェイタークラスを通じて料理を注文します。図2にその構造を示します。

ここに画像の説明を挿入します

package command;

import javax.swing.*;

public class CookingCommand {
    
    
    public static void main(String[] args) {
    
    
        Breakfast food1 = new ChangFen();
        Breakfast food2 = new HunTun();
        Breakfast food3 = new HeFen();
        Waiter fwy = new Waiter();
        fwy.setChangFen(food1);//设置肠粉菜单
        fwy.setHunTun(food2);  //设置河粉菜单
        fwy.setHeFen(food3);   //设置馄饨菜单
        fwy.chooseChangFen();  //选择肠粉
        fwy.chooseHeFen();     //选择河粉
        fwy.chooseHunTun();    //选择馄饨
    }
}

//调用者:服务员
class Waiter {
    
    
    private Breakfast changFen, hunTun, heFen;

    public void setChangFen(Breakfast f) {
    
    
        changFen = f;
    }

    public void setHunTun(Breakfast f) {
    
    
        hunTun = f;
    }

    public void setHeFen(Breakfast f) {
    
    
        heFen = f;
    }

    public void chooseChangFen() {
    
    
        changFen.cooking();
    }

    public void chooseHunTun() {
    
    
        hunTun.cooking();
    }

    public void chooseHeFen() {
    
    
        heFen.cooking();
    }
}

//抽象命令:早餐
interface Breakfast {
    
    
    public abstract void cooking();
}

//具体命令:肠粉
class ChangFen implements Breakfast {
    
    
    private ChangFenChef receiver;

    ChangFen() {
    
    
        receiver = new ChangFenChef();
    }

    public void cooking() {
    
    
        receiver.cooking();
    }
}

//具体命令:馄饨
class HunTun implements Breakfast {
    
    
    private HunTunChef receiver;

    HunTun() {
    
    
        receiver = new HunTunChef();
    }

    public void cooking() {
    
    
        receiver.cooking();
    }
}

//具体命令:河粉
class HeFen implements Breakfast {
    
    
    private HeFenChef receiver;

    HeFen() {
    
    
        receiver = new HeFenChef();
    }

    public void cooking() {
    
    
        receiver.cooking();
    }
}

//接收者:肠粉厨师
class ChangFenChef extends JFrame {
    
    
    private static final long serialVersionUID = 1L;
    JLabel l = new JLabel();

    ChangFenChef() {
    
    
        super("煮肠粉");
        l.setIcon(new ImageIcon("src/command/ChangFen.jpg"));
        this.add(l);
        this.setLocation(30, 30);
        this.pack();
        this.setResizable(false);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public void cooking() {
    
    
        this.setVisible(true);
    }
}

//接收者:馄饨厨师
class HunTunChef extends JFrame {
    
    
    private static final long serialVersionUID = 1L;
    JLabel l = new JLabel();

    HunTunChef() {
    
    
        super("煮馄饨");
        l.setIcon(new ImageIcon("src/command/HunTun.jpg"));
        this.add(l);
        this.setLocation(350, 50);
        this.pack();
        this.setResizable(false);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public void cooking() {
    
    
        this.setVisible(true);
    }
}

//接收者:河粉厨师
class HeFenChef extends JFrame {
    
    
    private static final long serialVersionUID = 1L;
    JLabel l = new JLabel();

    HeFenChef() {
    
    
        super("煮河粉");
        l.setIcon(new ImageIcon("src/command/HeFen.jpg"));
        this.add(l);
        this.setLocation(200, 280);
        this.pack();
        this.setResizable(false);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public void cooking() {
    
    
        this.setVisible(true);
    }
}

5.コマンドモードのアプリケーションシナリオ

システムの特定の操作にコマンドセマンティクスがあり、コマンドの実現が不安定(変更)である場合、コマンドモードを使用して、要求と実現を切り離すことができます。抽象コマンドインターフェイスは、リクエスターのコード構造を安定させ、レシーバーの特定のコマンドの実装の詳細をカプセル化するために使用されます。レシーバーと抽象コマンドは弱く結合されており(内部メソッドは一貫している必要はありません)、優れたスケーラビリティを備えています。

コマンドモードは通常、次のシナリオに適用されます

  1. 要求の呼び出し元を要求の受信者から切り離す必要がある場合、コマンドモードにより、呼び出し元と受信者が直接対話するのを防ぐことができます。
  2. システムがランダムにコマンドを要求したり、コマンドを頻繁に追加または削除したりすると、コマンドモードでこれらの機能を簡単に実現できます。
  3. システムが一連の操作を実行する必要がある場合、コマンドモードでこの機能を実現するためのマクロコマンドを定義できます。
  4. システムがコマンドの元に戻す(元に戻す)およびやり直し操作をサポートする必要がある場合、コマンドオブジェクトを保存してメモモードで実装できます。

6.コマンドモードの拡張

ソフトウェア開発では、コマンドモードは、以前に学習した組み合わせモードと組み合わせて使用​​されることがあります。これは、組み合わせコマンドモードとも呼ばれるマクロコマンドモードを構成します。マクロコマンドには、特定のコマンドと呼び出し元の2つの役割として機能するコマンドのグループが含まれています。マクロコマンドが実行されると、含まれるすべてのコマンドが再帰的に呼び出されます。具体的な構造図を図3に示します。

ここに画像の説明を挿入します

package command;

import java.util.ArrayList;

public class CompositeCommandPattern {
    
    
    public static void main(String[] args) {
    
    
        AbstractCommand cmd1 = new ConcreteCommand1();
        AbstractCommand cmd2 = new ConcreteCommand2();
        CompositeInvoker ir = new CompositeInvoker();
        ir.add(cmd1);
        ir.add(cmd2);
        System.out.println("客户访问调用者的execute()方法...");
        ir.execute();
    }
}

//抽象命令
interface AbstractCommand {
    
    
    public abstract void execute();
}

//树叶构件: 具体命令1
class ConcreteCommand1 implements AbstractCommand {
    
    
    private CompositeReceiver receiver;

    ConcreteCommand1() {
    
    
        receiver = new CompositeReceiver();
    }

    public void execute() {
    
    
        receiver.action1();
    }
}

//树叶构件: 具体命令2
class ConcreteCommand2 implements AbstractCommand {
    
    
    private CompositeReceiver receiver;

    ConcreteCommand2() {
    
    
        receiver = new CompositeReceiver();
    }

    public void execute() {
    
    
        receiver.action2();
    }
}

//树枝构件: 调用者
class CompositeInvoker implements AbstractCommand {
    
    
    private ArrayList<AbstractCommand> children = new ArrayList<AbstractCommand>();

    public void add(AbstractCommand c) {
    
    
        children.add(c);
    }

    public void remove(AbstractCommand c) {
    
    
        children.remove(c);
    }

    public AbstractCommand getChild(int i) {
    
    
        return children.get(i);
    }

    public void execute() {
    
    
        for (Object obj : children) {
    
    
            ((AbstractCommand) obj).execute();
        }
    }
}

//接收者
class CompositeReceiver {
    
    
    public void action1() {
    
    
        System.out.println("接收者的action1()方法被调用...");
    }

    public void action2() {
    
    
        System.out.println("接收者的action2()方法被调用...");
    }
}

クライアントは呼び出し元のexecute()メソッドに
アクセスします...受信者のaction1()メソッドが呼び出されます...
受信者のaction2()メソッドが呼び出されます...

おすすめ

転載: blog.csdn.net/saienenen/article/details/112674669