どちらの設計パターンもアルゴリズムをカプセル化し、実装の詳細を呼び出しクラスから分離します。私が認識できる唯一の違いは、Strategy パターンは実行用のパラメーターを受け取るのに対し、Command パターンはそうしないことです。
コマンドパターンは、作成時に実行のためのすべての情報が利用可能である必要があり、その呼び出しを遅らせることができるようです (おそらくスクリプトの一部として)。
どちらのパターンを使用するかの判断基準は何ですか?
どちらの設計パターンもアルゴリズムをカプセル化し、実装の詳細を呼び出しクラスから分離します。私が認識できる唯一の違いは、Strategy パターンは実行用のパラメーターを受け取るのに対し、Command パターンはそうしないことです。
コマンドパターンは、作成時に実行のためのすべての情報が利用可能である必要があり、その呼び出しを遅らせることができるようです (おそらくスクリプトの一部として)。
どちらのパターンを使用するかの判断基準は何ですか?
GoF 設計パターンのいくつかのカプセル化階層テーブルを含めて、これら 2 つのパターンの違いを説明します。うまくいけば、それぞれがカプセル化するものをよりよく示しているので、私の説明がより理にかなっています.
まず、階層には、表のどちら側から開始するかに応じて、特定のパターンを適用できるスコープ、または特定のレベルの詳細をカプセル化するために使用する適切なパターンがリストされます。
表からわかるように、ストラテジー パターン オブジェクトはアルゴリズムの実装の詳細を隠します。そのため、別のストラテジー オブジェクトを使用すると、同じ機能が異なる方法で実行されます。各戦略オブジェクトは、特定の要因に対して最適化されているか、他のパラメーターで動作している可能性があります。また、共通のインターフェースを使用することで、コンテキストはどちらでも安全に機能します。
コマンド パターンは、アルゴリズムよりもはるかに細かいレベルの詳細をカプセル化します。メッセージをオブジェクトに送信するために必要な詳細 (レシーバー、セレクター、および引数) をエンコードします。プロセス実行のこのような小さな部分をオブジェクト化することの利点は、そのようなメッセージを、その詳細をハードコーディングすることなく、一般的な方法でさまざまな時点または場所に沿って呼び出すことができることです。これにより、実行前に特定の呼び出しの詳細を知る必要なく、メッセージを 1 回以上呼び出したり、システムのさまざまな部分または複数のシステムに渡すことができます。
設計パターンでよくあることですが、パターン名を付けるためにすべての実装が詳細に同一である必要はありません。詳細は、実装によって異なり、オブジェクトでエンコードされるデータとメソッド引数としてエンコードされるデータが異なる場合があります。
Strategies encapsulate algorithms. Commands separate the sender from the receiver of a request, they turn a request into an object.
If it's an algorithm, how something will be done, use a Strategy. If you need to separate the call of a method from its execution use a Command. Commands are often used when you queue up messages for later use, like a task or a transaction.
非常に古い質問に答えます。(最も投票されたのではなく、最新の回答を見ている人はいますか?)
類似性があるため、これは正当な混乱です。戦略パターンとコマンド パターンの両方がカプセル化を利用します。しかし、それはそれらを同じにはしません。
主な違いは、カプセル化されているものを理解することです。両方のパターンが依存している OO の原則は、Encapsulate what varyです。
戦略の場合、変化するのはアルゴリズムです。たとえば、1 つの戦略オブジェクトは XML ファイルに出力する方法を知っており、もう 1 つの戦略オブジェクトは JSON などに出力します。さまざまなアルゴリズムがさまざまなクラスに保持 (カプセル化) されています。それはそれと同じくらい簡単です。
コマンドの場合、異なるのはリクエスト自体です。リクエストはFile Menu > Delete
またはRight Click > Context Menu > Delete
またはから送信されJust Delete Button pressed
ます。3 つのケースすべてで、同じタイプの 3 つのコマンド オブジェクトを生成できます。これらのコマンド オブジェクトは、3 つの削除要求のみを表します。削除アルゴリズムではありません。リクエストはオブジェクトの集まりなので、簡単に管理できます。突然、取り消しややり直しなどの機能を提供することは簡単になります。
コマンドが要求されたロジックをどのように実装するかは問題ではありません。execute() を呼び出すと、削除をトリガーするアルゴリズムを実装したり、他のオブジェクトに委任したり、戦略に委任したりすることさえできます。コマンドパターンの実装詳細のみです。これが、丁寧な要求方法ではありませんが、コマンドと名付けられた理由です:--)
それを戦略と対比してください。このパターンは、実行される実際のロジックのみに関係しています。これを行うと、最小限のクラス セットでさまざまな動作の組み合わせを実現できるため、クラスの爆発を防ぐことができます。
コマンドはカプセル化の理解を広げるのに役立ち、ストラテジーはカプセル化とポリモーフィズムの自然な使用を提供します。
私が見ているのは、同じことを行う方法が複数あり、それぞれが戦略であり、実行時に何かが実行される戦略を決定するということです。
たぶん、最初に StrategyOne を試してみてください。結果が十分でない場合は、StrategyTwo を試してください...
コマンドは、TryToWalkAcrossTheRoomCommand のように発生する必要がある個別の事柄にバインドされています。このコマンドは、オブジェクトが部屋を横切って行こうとするたびに起動されますが、その内部では、部屋を横切って行こうとするために StrategyOne と StrategyTwo が試行される場合があります。
マーク
私の意見では間違っているかもしれませんが、私はコマンドを実行する機能、または反応として扱います。少なくとも2人のプレーヤーが必要です。1人はアクションを要求し、もう1人はアクションを実行します。GUIは、コマンドパターンの典型的な例です。
コマンドは通常、特定のスコープまたはビジネスエリアに制限されますが、必須ではありませんexecute()
。1つのアプリケーション内で、請求書の発行、ロケットの起動、または同じインターフェイス(単一のメソッドなど)を実装するファイルの削除を行うコマンドがある場合があります。多くの場合、コマンドは自己完結型であるため、目的のタスクを処理するためにエグゼキュータから何も必要ありません(必要なすべての情報は構築時に提供されます)。コマンドはコンテキスト依存であり、このコンテキストを検出できる必要があります。 (Backspaceコマンドは、前の文字を正しく削除するためにテキスト内のキャレット位置を認識している必要があります。Rollbackコマンドは、ロールバックする現在のトランザクションを検出する必要があります; ...)。
戦略は少し異なります:それはいくつかの領域により拘束されています。この戦略では、日付をフォーマットする(UTC?ロケール固有?)(「日付フォーマッター」戦略)、または幾何学的図形の正方形を計算する(「正方形計算機」戦略)ルールを定義できます。戦略とは、この意味でフライ級のオブジェクトであり、何かを入力( "date"、 "figure"、...)として受け取り、それに基づいて何らかの決定を下します。おそらく最良ではありませんが、戦略の良い例は、javax.xml.transform.Source
インターフェースに接続されたものです。渡されたオブジェクトがであるDOMSource
か、SAXSource
またはStreamSource
戦略(=この場合はXSLTトランスフォーマー)がそれを処理するために異なるルールを適用するかどうかによって異なります。実装は単純なものにすることも、ChainofRe責任のパターンをswitch
含めることもできます。
しかし実際、これら2つのパターンには共通点があります。コマンドと戦略は、同じセマンティック領域内にアルゴリズムをカプセル化します。
指示:
基本コンポーネント:
execute()
ワークフロー:
クライアントはInvokerを呼び出します=> InvokerはConcreteCommandを呼び出します => ConcreteCommandは抽象Commandメソッドを実装するReceiverメソッドを呼び出します。
利点: クライアントは、コマンドおよびレシーバーの変更に影響されません。Invoker は、Client と Receiver の間の疎結合を提供します。同じ Invoker で複数のコマンドを実行できます。
コマンドパターンを使用すると、同じInvokerを使用して、異なるReceiverでコマンドを実行できます。Invoker はReceiverのタイプを認識していません
概念をよりよく理解するには、ウィキペディアのリンクに加えて、 Pankaj Kumarによるこの JournalDev の記事とJames Sugrueによるdzoneの記事を参照してください。
コマンドパターンを使用して
コマンドの呼び出し側と受信側を切り離す
コールバック メカニズムを実装する
元に戻す機能とやり直し機能を実装する
コマンドの履歴を維持する
java.lang.Thread
Commandパターンの 1 つの適切な実装です。Threadを呼び出し元として扱い、 RunnableをConcreteCommonad/Receiverとして実装し、run()
メソッドをCommandとして実装するクラスとして扱うことができます。
コマンド パターンの元に戻す/やり直しバージョンは、 Theodore Norvell の 記事で読むことができます。
ストラテジー:
戦略パターンは非常に理解しやすいです。ときにこのパターンを使用します。
アルゴリズムには複数の実装があり、アルゴリズムの実装は、特定の条件に応じて実行時に変更される可能性があります。
航空会社の予約システムの運賃コンポーネントの例を見てみましょう
航空会社は、ピーク月とオフピーク月の異なる期間に異なる運賃を提供したいと考えています。旅行のオフピーク時には、魅力的な割引を提供して需要を刺激したいと考えています。
戦略パターンの重要なポイント:
コード例を含む関連記事: