web-dev-qa-db-ja.com

戦略パターンとコマンドパターンの使用

どちらの設計パターンもアルゴリズムをカプセル化し、実装の詳細を呼び出し元のクラスから分離します。私が識別できる唯一の違いは、ストラテジーパターンは実行のためのパラメーターを受け取りますが、コマンドパターンは受け取りません。

コマンドパターンでは、作成時に実行に必要なすべての情報が必要であり、その呼び出しを(おそらくスクリプトの一部として)遅らせることができるように思えます。

1つのパターンを使用するか、他のパターンを使用するかを決定する決定は何ですか?

118
Extrakun

これら2つのパターンの違いを説明するために、いくつかのGoF設計パターンのカプセル化階層テーブルを含めています。うまくいけば、それぞれが何をカプセル化するのかをより良く説明できるので、私の説明はもっと理にかなっています。

最初に、階層には、テーブルのどちら側から開始するかに応じて、特定のパターンを適用できるスコープ、または詳細レベルをカプセル化するために使用する適切なパターンがリストされます。

design pattern encapsulation hierarchy table

表からわかるように、Strategy Patternオブジェクトはアルゴリズムの実装の詳細を隠しているため、異なる戦略オブジェクトを使用しても同じ機能を異なる方法で実行します。各戦略オブジェクトは、特定の要因に対して最適化されるか、他のパラメーターで動作します。そして、共通のインターフェースを使用することにより、コンテキストはどちらでも安全に機能します。

コマンドパターンは、アルゴリズムよりもはるかに小さな詳細レベルをカプセル化します。オブジェクトにメッセージを送信するために必要な詳細(レシーバー、セレクター、引数)をエンコードします。プロセス実行のこのような小さな部分をオブジェクト化することの利点は、そのようなメッセージを、詳細をハードコードすることなく、一般的な方法で異なる時点または場所に沿って呼び出すことができることです。実行前に特定の呼び出しの詳細を知る必要なく、メッセージを1回以上呼び出すか、システムの異なる部分または複数のシステムに渡すことができます。

設計パターンの典型であるように、パターン名を付けるためにすべての実装が詳細に同一である必要はありません。詳細は、実装およびオブジェクトでエンコードされるデータとメソッドの引数としてエンコードされるデータで異なります。

91
Huperniketes

戦略はアルゴリズムをカプセル化します。コマンドは、リクエストの送信者と受信者を分離し、リクエストをオブジェクトに変換します。

それがアルゴリズムである場合、何かが行われる方法は、戦略を使用します。メソッドの呼び出しをその実行から分離する必要がある場合は、コマンドを使用します。コマンドは、タスクやトランザクションなど、後で使用するためにメッセージをキューに入れるときによく使用されます。

48
Paul Rubel

非常に古い質問に答える。 (ほとんどの人が投票するのではなく、最新の回答を見ている人はいますか?)

類似性があるため、これは有効な混乱です。戦略パターンとコマンドパターンはどちらもカプセル化を利用します。しかし、それはそれらを同じにしません。

主な違いは、whatがカプセル化されていることを理解することです。 OO原則、両方のパターンが依存する、変化するものをカプセル化する

戦略の場合、変化するのはアルゴリズムです。たとえば、一方の戦略オブジェクトはXMLファイルへの出力方法を知っており、もう一方はJSONなどに出力します。異なるクラスでは異なるアルゴリズムが保持されます(カプセル化)。それはそれと同じくらい簡単です。

コマンドの場合、変化するのはrequest自体です。リクエストはFile Menu > DeleteまたはRight Click > Context Menu > DeleteまたはJust Delete Button pressed。 3つのケースはすべて、同じタイプの3つのコマンドオブジェクトを生成できます。これらのコマンドオブジェクトは、削除の3つの要求のみを表します。削除アルゴリズムではありません。リクエストはオブジェクトの束になっているため、簡単に管理できます。突然、元に戻すややり直しなどの機能を提供するのは簡単になります。

コマンドが要求されたロジックをどのように実装するかは問題ではありません。 execute()を呼び出すと、削除をトリガーするアルゴリズムを実装したり、他のオブジェクトに委任したり、戦略に委任することさえできます。これは、コマンドパターンの実装の詳細のみです。これがcommandという名前が付けられている理由です。requestへの丁寧な方法ではありません:-)

戦略と比較してください。このパターンは、実際に実行されるlogicのみに関係します。そうすれば、最小限のクラスセットでさまざまな動作の組み合わせを実現し、クラスの爆発を防ぐことができます。

ストラテジーはカプセル化とポリモーフィズムの自然な使用を提供する一方で、コマンドはカプセル化の理解を広げるのに役立つと思います。

25
rpattabi

私が見る方法は、同じことを行う複数の方法があり、それぞれが戦略であり、実行時に何かがどの戦略を実行するかを決定するということです。

最初にStrategyOneを試してみてください。結果が十分でない場合は、StrategyTwoを試してください...

コマンドは、TryToWalkAcrossTheRoomCommandのように発生する必要がある個別の事柄にバインドされています。このコマンドは、オブジェクトが部屋を横断しようとするたびに起動されますが、内部では、部屋を横断しようとするためにStrategyOneとStrategyTwoを試行する場合があります。

マーク

15
MStodd

私の意見では間違っているかもしれませんが、 command を機能実行、または反応として扱います。少なくとも2人のプレイヤーがいるはずです。アクションをリクエストするプレイヤーとアクションを実行するプレイヤーです。 GUIはコマンドパターンの典型的な例です。

  • アプリケーションツールバーのすべてのボタンは、何らかのアクションに関連付けられています。
  • この場合、ボタンはエグゼキューターです。
  • この場合のアクションはコマンドです。

通常、コマンドは何らかのスコープまたはビジネスエリアにバインドされますが、必ずしも必要ではありません。請求書を発行するコマンド、ロケットを起動するコマンド、または同じインターフェイス(たとえば、単一execute()メソッド)を実装するファイルを削除するコマンドがある場合があります応用。多くの場合、コマンドは自己完結型であるため、目的のタスクを処理するためにエグゼキューターから何も必要ありません(必要な情報はすべて構築時に提供されます)。コマンドはコンテキスト依存であり、このコンテキストを検出できる必要があります(Backspaceコマンドは、テキスト内のキャレット位置を認識して、前の文字を正しく削除する必要があります;Rollbackコマンドは、ロールバックする現在のトランザクションを検出する必要があります; ...)。

strategy は少し異なります。これは、ある領域によりバインドされています。戦略は、日付をフォーマットするルール(UTC?ロケール固有?)(「日付フォーマッター」戦略)または幾何学図形の平方を計算するルール(「平方計算機」戦略)を定義できます。この意味での戦略は、入力として何か(「日付」、「図」、...)を受け取り、それに基づいて何らかの決定を行うフライウェイトオブジェクトです。おそらく最善ではないかもしれませんが、戦略の良い例はjavax.xml.transform.Sourceインターフェース:渡されたオブジェクトがDOMSourceであるかSAXSourceであるかStreamSourceであるかに応じて、戦略(=この場合XSLTトランスフォーマー)は異なるルールを適用して処理します。実装は、単純なswitchにすることも、 責任の連鎖パターン を含めることもできます。

しかし実際、これらの2つのパターンには共通点があります。コマンドと戦略は、同じセマンティック領域内でアルゴリズムをカプセル化します。

7
dma_k

コマンド:

基本コンポーネント:

  1. Commandexecute()のような抽象コマンドのインターフェースを宣言します
  2. Receiverは特定のコマンドの実行方法を知っている
  3. InvokerConcreteCommandを保持し、実行する必要があります
  4. ClientConcreteCommandを作成し、Receiver
  5. ConcreteCommandは、CommandReceiverの間のバインディングを定義します

ワークフロー:

Client呼び出しInvoker=> Invoker呼び出しConcreteCommand=>ConcreteCommandReceiverメソッドを呼び出します。これは、抽象Commandメソッドを実装します。

Advantage:クライアントはCommandおよびReceiverの変更に影響しません。呼び出し側は、クライアントとレシーバーの間の疎結合を提供します。同じInvokerで複数のコマンドを実行できます。

Commandパターンにより、異なるReceiversでコマンドを実行できます。同じInvokerを使用します。呼び出し側はReceiverのタイプを認識しません

概念をよりよく理解するには、このJournalDevをご覧ください 記事 byPankaj Kumarおよびdzone 記事 byJamesウィキペディアのリンクに加えて、Sugrue

Commandパターンを使用して、

  1. コマンドの呼び出し側と受信側を分離する

  2. コールバックメカニズムを実装する

  3. 元に戻す機能とやり直し機能を実装する

  4. コマンドの履歴を維持する

Java.lang.Threadは、Commandパターンの優れた実装の1つです。 Threadを呼び出し側として、およびRunnableを実装するクラスとしてConcreteCommonad/Receiverおよびrun()メソッドとしてCommand

コマンドパターンの元に戻す/やり直しバージョンは、Theodore Norvellの記事 で読むことができます。

戦略:

戦略パターンは非常に簡単に理解できます。次の場合にこのパターンを使用します

アルゴリズムには複数の実装があり、アルゴリズムの実装は特定の条件に応じて実行時に変更できます

航空会社の予約システムの運賃コンポーネントの例を挙げてください

航空会社は、異なる期間(ピーク月とオフピーク月)に異なる運賃を提供したいと考えています。オフピーク旅行の日には、魅力的な割引を提供して需要を刺激したいと考えています。

Strategyパターンの重要なポイント:

  1. それは行動パターンです
  2. 委任に基づいています
  3. メソッドの動作を変更することにより、オブジェクトの内臓を変更します
  4. アルゴリズムのファミリーを切り替えるために使用されます
  5. 実行時にオブジェクトの動作を変更します

コード例のある関連記事:

コマンドデザインパターンを使用

戦略パターンの実世界の例

4
Ravindra babu

私にとって、違いは意図の一つです。両方のパターンの実装は非常に似ていますが、異なる目的があります。

  • ストラテジーの場合、オブジェクトを使用するコンポーネントは、オブジェクトがwhatを知っている(そしてそれを使用して独自の作業の一部を実行する)が、気にしないhowそれをします。

  • コマンドの場合、オブジェクトを使用するコンポーネントはwhatがコマンドを実行することもhowを実行することも認識しません。それを呼び出す方法を知っているだけです。呼び出し元のタスクは、コマンドを実行することです-コマンドによって実行される処理は、呼び出し元のコア作業の一部を形成しません。

これが違いです-コンポーネントを使用するオブジェクトは、実際にコンポーネントが何をするかを知っているか、気にしているのですかほとんどの場合、これはパターンオブジェクトが呼び出し元に値を返すかどうかに基づいて決定できます。呼び出し側がパターンオブジェクトが何をするかを気にする場合、おそらく何かを返すことを望み、それが戦略になります。戻り値を気にしない場合、おそらくコマンドです(注:Java Callableは、値を返しますが、呼び出し側は気にしないため、Callableは依然としてコマンドです値について-最初にコマンドを提供したものにそれを返します)。

0
BarrySW19