web-dev-qa-db-ja.com

コマンドパターン:パラメーターをコマンドに渡す方法

私の質問は、次の抽象化(C#コード)があるコマンドパターンに関連しています。

public interface ICommand
{
    void Execute();
}

アプリケーションからエンティティを削除することを目的とした簡単な具体的なコマンドを見てみましょう。たとえば、Personインスタンス。

DeletePersonCommandを実装するICommandを用意します。このコマンドには、Personメソッドが呼び出されたときに削除するために、パラメーターとして削除するExecuteが必要です。

パラメータ化されたコマンドを管理する最良の方法は何ですか?コマンドを実行する前にコマンドにパラメーターを渡す方法は?

59
Romain Verdier

コンストラクターまたはセッターインジェクション(または同等の方法)によって、パラメーターをコマンドオブジェクトに関連付ける必要があります。おそらくこのようなもの:

public class DeletePersonCommand: ICommand
{
     private Person personToDelete;
     public DeletePersonCommand(Person personToDelete)
     {
         this.personToDelete = personToDelete;
     }

     public void Execute()
     {
        doSomethingWith(personToDelete);
     }
}
60
Blair Conrad

コンストラクターまたはセッターを介してデータを渡すことはできますが、コマンドの作成者はコマンドが必要とするデータを知っている必要があります...

「コンテキスト」のアイデアは本当に良いものであり、私は以前にそれを活用する(内部)フレームワークに取り組んでいました。

コントローラー(ユーザーと対話するUIコンポーネント、ユーザーコマンドを解釈するCLI、受信パラメーターとセッションデータを解釈するサーブレットなど)を設定して、使用可能なデータへの名前付きアクセスを提供する場合、コマンドは必要なデータを直接要求できます。

私はこのようなセットアップが可能にする分離が本当に好きです。次のように階層化について考えます。

User Interface (GUI controls, CLI, etc)
    |
[syncs with/gets data]
    V
Controller / Presentation Model
    |                    ^
[executes]               |
    V                    |
Commands --------> [gets data by name]
    |
[updates]
    V
Domain Model

これを「正しく」行うと、同じタイプのユーザーインターフェイスで同じコマンドとプレゼンテーションモデルを使用できます。

これをさらに一歩進めると、上記の「コントローラー」はかなり一般的です。 UIコントロールは、呼び出すコマンドのnameを知るだけで済みます-それら(またはコントローラー)は、そのコマンドの作成方法やコマンドのデータを知る必要はありません。ニーズ。ここが本当の利点です。

たとえば、実行するコマンドの名前をマップで保持できます。コンポーネントが「トリガー」されると(通常actionPerformed)、コントローラーはコマンド名を検索し、インスタンス化し、executeを呼び出し、アンドゥスタックにプッシュします(使用する場合)。

21

いくつかのオプションがあります:

プロパティまたはコンストラクターでパラメーターを渡すことができます。

その他のオプションは次のとおりです。

interface ICommand<T>
{
    void Execute(T args);
}

そして、すべてのコマンドパラメーターを値オブジェクトにカプセル化します。

10
Juanma

コマンドオブジェクトを作成するときに人を渡します。

ICommand command = new DeletePersonCommand(person);

そのため、コマンドを実行するときに、知っておく必要のあるすべてのことをすでに知っているようになります。

class DeletePersonCommand : ICommand
{
   private Person person;
   public DeletePersonCommand(Person person)
   {
      this.person = person;
   }

   public void Execute()
   {
      RealDelete(person);
   }
}
6
jop

私の実装はこれです(Juanmaによって提案されたICommandを使用して):

public class DeletePersonCommand: ICommand<Person>
{
    public DeletePersonCommand(IPersonService personService)
    {  
        this.personService = personService;
    }

    public void Execute(Person person)
    {
        this.personService.DeletePerson(person);
    }
}

IPersonServiceはIPersonRepositoryである可能性がありますが、それはコマンドの「レイヤー」に依存します。

6
bloparod

この場合、Commandオブジェクトで行ったのは、本質的にマップであるContextオブジェクトを作成することです。マップには名前と値のペアが含まれており、キーは定数であり、値はコマンドの実装で使用されるパラメーターです。後のコマンドが前のコマンドからのコンテキストの変更に依存する一連のコマンドがある場合に特に便利です。

実際の方法は

void execute(Context ctx);
5
user12786

コンストラクターで、フィールドとして保存されます。

また、最終的には、Undoスタックまたはファイルの永続化のためにICommandをシリアル化できるようにする必要があります。

4
Frank Krueger

C#/ WPFのパターンに基づいて、ICommandインターフェイス(System.Windows.Input.ICommand)は、ExecuteおよびCanExecuteメソッドのパラメーターとしてオブジェクトを取得するように定義されています。

interface ICommand
            {
                bool CanExecute(object parameter);
                void Execute(object parameter);
            }

これにより、ICommandを実装するカスタムコマンドオブジェクトのインスタンスである静的パブリックフィールドとしてコマンドを定義できます。

public static ICommand DeleteCommand = new DeleteCommandInstance();

このようにして、executeが呼び出されると、関連するオブジェクト(ユーザーの場合)が渡されます。その後、Executeメソッドはオブジェクトをキャストし、Delete()メソッドを呼び出すことができます。

public void Execute(object parameter)
            {
                person target = (person)parameter;
                target.Delete();
            } 
3
TheZenker

使用するパラメーターを含むCommandArgsオブジェクトを作成する必要があります。 Commandオブジェクトのコンストラクターを使用してCommandArgsオブジェクトを注入します。

1
David Robbins

DeletePersonCommandは、コンストラクターまたはメソッドにパラメーターを持つことができます。 DeletePersonCommandにはExecute()があり、executeでは、Getter/Setterが以前にExecute()の呼び出しに渡す属性をチェックできます。

0

必要な引数をDeletePersonCommandのコンストラクターに追加します。次に、Execute()が呼び出されると、構築時にオブジェクトに渡されたパラメーターが使用されます。

0
Matt Dillard