コマンドにどれだけのロジックを入れることができますか?または別の言い方をすると、コマンドパターンはどのような種類のロジックですか?


8

私はかなり前からコマンドパターンを使用していますが、実際にExecuteメソッドにどれだけのロジックを入れることができるか本当にわかりません。

コマンドパターンの現在の実装は次のようになります。

public abstract class Command
{
    public static event EventHandler Completed = delegate { };        

    public bool Success { get; private set; }

    public Exception Exception {get; private set; }

    public abstract bool Execute();

    protected bool OnCompleted(bool success, Exception ex = null)
    {       
        Success = success;
        Exception = ex;
        Completed(this, EventArgs.Empty)
        return success;
    }
}

そしてこれらは私が自分自身に尋ねる(そして私のコマンドで練習する)質問です:

  1. メッセージボックスやファイルを開くダイアログなどを表示してもよいですか?
  2. オブジェクトのプロパティを設定することはできますか?
  3. コマンドにビジネスロジックを含めることはできますか?
  4. コマンドはとにかくGUIコントロールを変更できますか?
  5. どのレイヤーコマンドが属していますか?ビューまたはデータレイヤー?両方のレイヤーでコマンドを使用できますか?
  6. コマンドは、以前にあったすべてのことを実行できますbutton1_Clickか?
  7. 単体テスト可能なコマンドである必要がありますか?
  8. コマンドは、APIを使用し、アプリケーションの最後の層を構築し、例外をキャッチする最後の手段でもあるユーザーと見なすことができますか?
  9. コマンドはコードでも実行できますか(コマンドはapiを呼び出し、apiは実行され、最後に他のいくつかのapiがコマンドを呼び出します)、ユーザーのみがそれを呼び出すことができ、APIはそれらの存在を知らないはずです。
  10. MVC、MVVC、またはコントローラーを使用したその他のデザインパターンでコマンドを実行する場所はありますか?それらは相互に排他的であるようです。何が望ましいですか?

コマンドパターンを実装する方法を示すチュートリアルはたくさんありますが、実際のウォルドアプリケーションに適用する方法については実際には説明されていません。

回答:


7

コマンドパターンは通常、WHATをWHOから切り離し、WHATをWHENから切り離すために使用されます。これは、次のように単純なインターフェースを持つことの利点です。

public abstract class Command {
    public abstract void execute();
}

あなたがクラスを持っていると想像してみましょうEngineOnCommand。このコマンドを、コマンドのインスタンスを受け入れる他のオブジェクトに渡すことができます。つまり、これは、このEngineOnCommandを受け取るクラスが、別のコマンドを受け取って実行することもでき、それを知ってはならないということです。これは、WHOからWHATを分離したことを意味します。

さて、コマンドPattermための第二の場合は、デカップルにあるWHATからWHEN。データベースのアクションが夜間にのみ実行され、要求されたシーケンスに従う必要があるシステムを作成するとします。1つずつ実行できるコマンドのキューを使用すると、これを実現できます。アイデアは、コマンドの呼び出しが実際に後で実行されるリスト上のコマンドのエンキューのみをトリガーするということです。

上記の私の例がパターンのアイデアを理解するのに役立つことを願っています。ここで、上記をベースにして、いくつかの質問に答えてみます。

コマンドにビジネスロジックを含めることはできますか?

はい、それはそれを含むべきだと思います。WHOとWHENの分離された方法で含まれます。

コマンドはとにかくGUIコントロールを変更できますか?

これは、それをGUIに結合していることを意味します。これは、WHOからWHATを分離する目的で使用されていないことを意味します。コマンドがGUIまたはバッチ機能である場合、コマンドは誰がそれを呼び出しているのかを知るべきではありません。

単体テスト可能なコマンドである必要がありますか?

それはすべきではありません、それはユニットテスト可能なければなりません。すべてのコードは単体テスト可能でなければなりませんが、理想的にはすべてのコマンドが単体テスト可能でなければなりません。

すべての質問に答えるのではなく申し訳ありませんが、GOFの本を確認してください。良い例がいくつか含まれています。


+1すばらしい答えですが、ちょっとしたこつこつですが、ユニットテストなどの特定のプラクティスを推奨することは素晴らしいですが、それらのプラクティスを普遍的に必須であるかのように指揮することは、若いプログラマーには役立たない傾向があります。経験から言えば。
Phil

2

コマンドの利点のほとんどは、アクションを元に戻したり、やり直したり、複数の場所で(ネットワーク接続を介して)アクションを実行したり、後で実行したりすることが簡単にできることです。

それを念頭に置いて:

  1. おそらく違います。ダイアログボックスを「元に戻す」ことが理にかなっている状況を想像するのは難しく、これは、UIコードに入れたい種類のことです。

  2. コマンドを実行する可能性のあるすべての場所からそのオブジェクトにアクセスできる限り、当然、そのプロパティを変更してもかまいません。

  3. もちろんです。これは、モデルにビジネスロジックを「含める」必要があるかどうかに関係します。実際には、ビジネスロジックが少なすぎるアンチパターン(「貧血ドメインモデル」)と見なされています。

  4. GUIコントロールがモデルの一部である場合は、絶対です。そうでなければ、それは疑わしいです。

  5. 間違いなくビューではありません。データまたはモデルが変更され、それに応じて反応することをビューに通知する必要がありますが、実際の変更はデータまたはモデルで行われる必要があります。

  6. 原則として、おそらくはい。コマンドを元に戻すことができることは、パターンの最も優れた点の1つです。

  7. コマンドのexecute()関数は必ずユニットテストする必要があります。繰り返しますが、コマンドが何らかのモデルに関連付けられている場合、それらはアプリケーションのテストが最も容易な部分の1つです。

  8. どういう意味かよくわかりませんが、

    • 外部APIがコマンドを入力または出力として使用することは完全に問題ありません。もちろん、それらを検証すると仮定します。
    • 「最後のレイヤー」は私にとってビューのように聞こえます。MVCでは、コマンドはおそらくコントローラーによって生成され、モデルによって処理される必要があるため、ビューがコマンドの「上」に構築されていると考えるのが賢明でしょう。
    • 例外については、おそらくそうではありません。MVCでは、コントローラーが例外をキャッチし、それらを適切な種類のダイアログボックスに変えるものになると思います。モデル/コマンドではありません。
  9. もちろんです。コードがコマンドを実行しない場合、コマンドの利点のほとんどは実装するのが不可能に近いものです。

  10. それはおそらく今では明白ですが、それらを相互に排他的であるとはまったく見なしていません。私のチームでは、コントローラーがユーザーが生成したイベントをコマンドに変換し、モデルがコマンドを受信して​​実行し(そして「元に戻す」コマンドをどこかに保存します)、完了したら、コントローラーはビューに新しいモデルに基づいて自身を更新するように指示します。コマンドは、ネットワークを介して他のユーザーが表示しているモデルのコピーにも送信されるため、他のユーザーにも表示されます。それは見事に機能します。

そしてタイトルの質問:1つのコマンドにどのくらいのロジックを入れるか?私はそれに最小値または最大値を設定しません。私が言えることは、一連のより単純なコマンドを使用してより複雑なコマンドを実装し、関数をリファクタリングするのと同じ方法でコマンドをリファクタリングすることは非常に良いアイデアだということです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.