序文
動作設計パターンは、複数のクラスまたはオブジェクトがどのように連携して、単一のオブジェクトでは個別に完了できないタスクを完了するかを記述することです
以前のテンプレートメソッドのパターンは、操作フローのスケルトンを与え、サブクラスで実装される特定のステップを遅らせることです
(ゲストプロセス中に実行されるものはサブクラスによって記述され、親クラスはゲストプロセスのみを記述します:order- "eat-"有料注文)
以下はコマンドモードです
現実の問題
ソフトウェア開発システムでは、「メソッドリクエスタ」と「メソッドインプリメンタ」の間に密接な結合関係があることがよくあります(クラスが別のクラスのメソッドを呼び出すため、操作上の結合が高くなります)。これは、ソフトウェア機能の拡張と保守には役立ちません。
考えてみてください。それがカップリング関係であり、「元に戻す、やり直し、記録」およびその他の処理を複数の動作で実行したい場合は、どうすればよいですか?
すべての操作を記録する必要がありますか?
したがって、「メソッドリクエスタをメソッドインプリメンタから分離する方法」は非常に重要になります。
栗を与えます。
これはライトをオン/オフにするだけでなく、キャンセルするための多目的リモコンです
コマンドモードを使用することでよりよく解決できます
コマンドモード
コマンドパターン:リクエストをオブジェクトとしてカプセル化します。これにより、さまざまなリクエストを使用して顧客をパラメータ化したり、リクエストをキューに入れたり、リクエストログを記録したり、可逆的な操作をサポートしたりできます。
コマンドモードはオブジェクト動作モードであり、そのエイリアスはアクションモードまたはトランザクションモードです。
コマンドモードのモードの動機:コマンドモードでは、送信者と受信者を完全に切り離すことができます。送信者と受信者の間に直接の参照関係はありません。要求を送信するオブジェクトは、要求の送信方法を知るだけで、要求を完了する方法を知る必要はありません。
コマンドモードの構造:
モードの役割:
- コマンド:コマンドを実行するためのインターフェースを宣言する抽象コマンドクラス
- ConcreteCommand:具象コマンドクラス。抽象コマンドクラスの実装であり、レシーバーオブジェクトを持ち、レシーバーの関数を呼び出すことにより、コマンドによって実行される操作を完了します。
- 呼び出し側:リクエストの送信者であるリクエスターは通常、多くのコマンドオブジェクト(コレクションに格納できる)を持ち、コマンドオブジェクトにアクセスすることで関連するリクエストを実行します。レシーバーには直接アクセスしません。
- レシーバー:レシーバー:ビジネスの実際の実行者で、特定のコマンドを実行します
コマンドモードの場合
コマンドモードでリモコンをシミュレートし、リモコンボタンでライトスイッチを制御します
package com.company.Behavioral.Command;
import java.util.ArrayList;
import java.util.List;
//动作执行对象
class LightReceiver{
public void on(){
System.out.println("电灯打开了。。。");
}
public void off(){
System.out.println("电灯关闭了。。。");
}
}
//抽象命令
interface Command {
//执行动作
public void execute();
//撤销动作
public void undo();
}
//具体命令对象:开灯命令对象
class LightOnCommend implements Command{
//聚合LightReceiver
private LightReceiver lightReceiver;
public LightOnCommend(LightReceiver lightReceiver) {
this.lightReceiver = lightReceiver;
}
//当前是开灯命令,执行动作是开灯
@Override
public void execute() {
lightReceiver.on();
}
//当前是开灯命令,撤销动作是关灯
@Override
public void undo() {
lightReceiver.off();
}
}
//具体命令对象:关灯命令对象
class LightOffCommend implements Command{
//聚合LightReceiver
private LightReceiver lightReceiver;
public LightOffCommend(LightReceiver lightReceiver) {
this.lightReceiver = lightReceiver;
}
//当前是关灯命令,执行动作是关灯
@Override
public void execute() {
lightReceiver.off();
}
//当前是关灯命令,撤销动作是关开灯
@Override
public void undo() {
lightReceiver.on();
}
}
//空命令对象:空执行,用于初始化每个按钮
//空对象模式,可以省掉对空的判断
class NoCommend implements Command{
@Override
public void execute() {
}
@Override
public void undo() {
}
}
//遥控器
class RemoteController{
//存储命令
private List<Command> onCommands ;
private List<Command> offCommands ;
//用于撤销的命令,需要记录执行命令
private Command undoCommand;
//构造器,初始化
public RemoteController() {
onCommands = new ArrayList<>();
offCommands = new ArrayList<>();
//按照遥控器的按钮数,进行初始化,直接加入空命令即可
for (int i=0;i<2;i++){
onCommands.add(i,new NoCommend());
offCommands.add(i,new NoCommend());
}
}
//给按钮设置需要的命令
public void setCommand(int no,Command onCommand,Command offCommand){
onCommands.add(no,onCommand);
offCommands.add(no,offCommand);
}
//如果按下开按钮
public void onButtonPush(int no){
//找到按下的按钮,执行该按钮的执行方法
onCommands.get(no).execute();
//记录这次操作,用于撤销
undoCommand = onCommands.get(no);
}
//如果按下关按钮
public void offButtonPush(int no){
//找到按下的按钮,执行该按钮的执行方法
offCommands.get(no).execute();
//记录这次操作,用于撤销
undoCommand = offCommands.get(no);
}
//如果按下撤销按钮
public void undoButtonPush(int no){
//找到按下的按钮,执行该按钮的执行方法
undoCommand.undo();
}
}
//客户
class Client{
public static void main(String[] args) {
//使用命令模式,通过遥控器,控制灯
//创建电灯对象(真正的操作执行者)
LightReceiver lightReceiver = new LightReceiver();
//创建电灯的开关命令
LightOnCommend lightOnCommend = new LightOnCommend(lightReceiver);
LightOffCommend lightOffCommend = new LightOffCommend(lightReceiver);
//遥控器
RemoteController remoteController = new RemoteController();
//给遥控器设置相关命令
//no = 0 是电灯的开关操作
remoteController.setCommand(0,lightOnCommend,lightOffCommend);
//no = 1 是电视的开关操作(还未实现)
//按灯的按钮
System.out.println("--------按下灯的开按钮-----------");
remoteController.onButtonPush(0);
System.out.println("--------按下灯的关按钮-----------");
remoteController.offButtonPush(0);
System.out.println("---------按下撤销按钮------------");
remoteController.undoButtonPush(0);
}
}
上記のリモコンはコマンドモードで実現され、各リモコンのボタンにコマンドオブジェクトが割り当てられており、ボタンを押すたびにコマンドオブジェクトの実行操作が呼び出され、記録されて操作のキャンセルに使用できます。
パターン分析
コマンドモードはリクエスタとエグゼキュータの完全な分離を実現しますが、支払われる価格はシステムのクラス爆発です
- コマンドモードの本質は、コマンドをカプセル化し、コマンドを発行する責任とコマンドを実行する責任を分離することです。
- 各コマンドは操作です。要求側は操作を実行する要求を送信し、受信側は要求を受信して操作を実行します。
- コマンドモードでは、要求側と受信側を独立させることができるため、要求側は、要求側のインターフェイス、要求の受信方法、操作が実行されるかどうか、いつ実行されるか、および方法を知る必要がありません。実行された
- コマンドモードでは、リクエスト自体がオブジェクトになり、他のオブジェクトと同じように保存および転送できます。
- コマンドモードの鍵は、抽象コマンドインターフェイスが導入され、送信者が抽象コマンドインターフェイスをプログラミングしていることです。抽象コマンドインターフェイスを実装する特定のコマンドのみを受信者に関連付けることができます。
上記の場合、コマンドモードの実行プロセス:シーケンス図を使用して、
コマンドモードの長所と短所
アドバンテージ
- システムの結合を減らし、コマンドリクエスターとエグゼキューターを完全に分離します。
- 新しいコマンドをシステムに簡単に追加できます。TVコントロールを追加する場合は、TVコントロールエグゼキューターと2つの特定のスイッチコマンドクラスを設定する必要があります。
- コマンドキューとマクロコマンド(結合されたコマンド)を設計するのは比較的簡単です。結局のところ、リモートコントロール(リクエスター)にコマンドオブジェクトを格納するコレクション(ArrayList)を維持することで、キューを設計するだけで済みます。
- コマンドはオブジェクトであり、レコードを簡単に保存できるため、元に戻すとやり直しをリクエストに簡単に実装できます。
短所
致命的な欠点:コマンドモードを使用すると、一部のシステムで特定のコマンドクラスが多すぎて、クラスが急増する
前の設計の単純なケースでは、コマンドインターフェイス、2つの特定のコマンドクラス、コマンドエグゼキューター、コマンドリクエスター、およびクライアントが使用されています
(ひどいので、非常に多くのタイプ...
TVスイッチを追加する場合は、コマンドエグゼキューターと2つの特定のコマンドクラスを追加する必要があります。
カップリングの程度は低いですが、システムリソースの消費にコストがかかります
該当するシーン
- 実行者と要求者が直接対話しないように、システムは要求者と実行者を分離する必要があります
- システムはリクエストを指定し、リクエストをキューに入れ、リクエストを異なる時間に実行する必要があります
- システムはコマンドの取り消し(元に戻す)操作とやり直し(やり直し)操作をサポートする必要がある
- システムは一連の操作をグループ化する必要があります。つまり、マクロコマンドをサポートします。
もちろん、コマンドモードを使用するかどうかの選択は、上のあなたを見て、システムリソース、プログラミングの複雑さとのカップリングの選択肢
実際の使用
- Java言語はコマンドモードを使用して、AWT / Swing GUIの委任イベントモデル(DEM)を実装します
- Spring Framework JdbcTemplateアプリケーション
- 多くのシステムは、UNIXプラットフォームでのシェルプログラミングなどのマクロコマンド機能を提供します。これは、コマンドオブジェクトに複数のコマンドをカプセル化でき、コマンドシーケンスを実行するための単純なコマンドのみが必要です。
マクロコマンド
実際、私たちは全員、マウスマクロなどのマクロコマンドに触れている必要があります。
マクロコマンドには、特定のコマンドと呼び出し元の2つの役割として機能する一連のコマンドが含まれています。マクロコマンドを実行すると、それに含まれるすべてのコマンドが再帰的に呼び出されます。
これはコマンドモードと組み合わせモードの使用です
リクエストの呼び出し元を使用したときに、1つのonコマンドonCommendを保存するためと、もう1つはコマンドOffCommendを保存するために2つのArrayListを使用したことに気づきましたか。
リクエストの呼び出し元自体がコマンド(Commendインターフェースを実装)である場合、その実行メソッドは、ArrayListの各コマンドの実行を実行するように設定されます。これは、結合モードのコンテナーオブジェクトではありませんか?
(外観モードのアイデアも少し感じられますが、主な焦点は「全体と部分」とエグゼキュータとリクエスタの間のデカップリングです。)
組み合わせモードが見られるかどうかは明らかではありません:デザインモード(12)構造モード-組み合わせモード
マクロコマンドは複合コマンドとも呼ばれ、その構造図:
CompositeCommand executeの実行メソッドは、ArrayListのコマンドを呼び出すforeachループのexecuteメソッドです。
まとめ
- コマンドモード:リクエストをオブジェクトにカプセル化します。さまざまなリクエストオブジェクトを使用して、クライアントをパラメーター化し、リクエストをキューに入れたり、リクエストログを記録したり、可逆操作をサポートしたりできます。
- コマンドモードには4つの役割があります。抽象コマンドクラス、特定のコマンドクラス、コマンドエグゼキュータ、コマンドリクエスタです。
- 抽象コマンドクラス(インターフェイス)は、コマンドに含める必要のあるメソッドを指定します
- 特定のコマンドクラスは、抽象コマンダーを実装し、エグゼキューターを集約し、抽象コマンドクラスのメソッドを実装します(実際にはエグゼキューターによって実行されます)。
- 実行者:コマンドを実行する操作、特定のビジネス実行者
- コマンドリクエスター:リクエストの送信者は、コマンドオブジェクト(エグゼキューターから分離)を介してリクエストを実行します
- コマンドモードの利点:コマンドリクエスタとエグゼキュータが結合され、強力な拡張性があり、マクロコマンドを設計でき、引き出し操作を簡単に実行できます。
- コマンドモードの短所:システムには特定のコマンドクラスが多すぎる
- コマンドモードは、次の場合に適しています。コマンドリクエスタとコマンドエグゼキュータを分離して、リクエスタとエグゼキュータが直接対話しないようにする必要があります。リクエストを異なる時間に指定し、リクエストをキューに入れて実行する必要があります。元に戻す操作とコマンドのリカバリをサポートする必要があります。操作。操作のグループをグループ化する必要があります。つまり、マクロコマンドをサポートします。
- マクロコマンドは、コマンドモードと結合モードを組み合わせたものであり、コマンドリクエスタが抽象コマンドクラス(これもコマンド)を実装し、いくつかのコレクションストレージコマンドオブジェクトを維持し、その実行メソッドは、コレクション内のコマンドオブジェクトの実行メソッドを呼び出すことができます。