web-dev-qa-db-ja.com

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

私はかなり前からコマンドパターンを使用していますが、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を利用するserと見なすことはできますかbuildsアプリケーションの最後のレイヤーであり、例外をキャッチする最後の手段でもありますか?
  9. コマンドはコードによっても実行できますか(コマンドがapiを呼び出し、apiが実行され、最後に他のいくつかのapiがコマンドを呼び出します)、ユーザーのみがそれを呼び出すことができ、APIはそれらの存在を知らないはずです。
  10. MVC、MVVC、またはコントローラーを使用したその他のデザインパターンのコマンド用の場所はありますか?それらは相互に排他的であるようです。何が望ましいですか?

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

8
t3chb0t

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

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

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

ここで、コマンドPattermの2番目のケースは、[〜#〜][〜#〜]いつ[〜#〜]。データベース内のアクションが夜間にのみ実行され、要求されたシーケンスに従う必要があるシステムを作成するとします。 1つずつ実行できるコマンドのキューを使用すると、これを実現できます。アイデアは、コマンドの呼び出しが実際に後で実行されるリスト上のコマンドのエンキューをトリガーするだけであるということです。

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

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

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

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

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

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

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

すべての質問に答えられなかったのは残念ですが、 [〜#〜] gof [〜#〜] の本を確認してください。良い例がいくつか含まれています。

7
pietromenna

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

それを念頭に置いて:

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

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

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

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

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

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

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

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

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

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

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

2
Ixrec