34

戦略パターンはいつ使用されますか?

次のようなクライアント コード スニペットが表示されます。


class StrategyExample {

    public static void main(String[] args) {

        Context context;

        // Three contexts following different strategies
        context = new Context(new ConcreteStrategyAdd());
        int resultA = context.executeStrategy(3,4);

        context = new Context(new ConcreteStrategySubtract());
        int resultB = context.executeStrategy(3,4);

        context = new Context(new ConcreteStrategyMultiply());
        int resultC = context.executeStrategy(3,4);

    }

}

これを次のようにリファクタリングできるようです。


class StrategyExample {

    public static void main(String[] args) {
         // Three contexts following different strategies
        int resultA =new ConcreteStrategyAdd().execute(3,4);
        int resultB =new ConcreteStrategySubtract().execute(3,4);
        int resultC =new ConcreteStrategyMultiply().execute(3,4);
    }

}

コードの最初のセクションは、ウィキペディアのページから直接取得されました。大きな違いの 1 つは、コンテキストがなくなることですが、この例では何もしていません。誰かが戦略が理にかなっているより良い例を持っているかもしれません。私は通常、デザイン パターンが好きですが、これは有用性を追加することなく複雑さを追加しているようです。

4

10 に答える 10

72

このようなおもちゃの例の問題点は、要点を見落としやすいことです。この場合、コードは実際に示したように実装できます。戦略パターンにおける主な価値は、さまざまな状況でさまざまな実装を切り替えることができることにあります。

あなたが持っている例は、パターン内のオブジェクトとそれらの間の相互作用のみを示しています。代わりに、デスクトップかスマートフォンかに応じてウェブサイトのグラフをレンダリングするコンポーネントがあり、作成したブラウザのタイプを検出し、別のコンポーネントで戦略を設定するコードがあると想像してください。複製する必要のない複雑なコードで戦略オブジェクトを使用し、適切な戦略オブジェクトにグラフの実際の描画の詳細を残して、両方の状況で作業を行います。

interface GraphStrategy {
    Image renderGraph(Data graphData);
}

class BigGraphStrategy implements GraphStrategy {
    ...
}

class SmallGraphStrategy implements GraphStrategy {
    ...
}

次に、他のコードで:

GraphStrategy graphStrategy;

if (phoneBrowser == true) { 
    graphStrategy = new SmallGraphStrategy();
} else {
    graphStrategy = new BigGraphStrategy();
}

アプリケーション コードの残りの部分は、graphStrategy.renderGraph()実行されているイメージ レンダリングがフルかスモールかを知らなくてもそのまま使用できます。

于 2009-11-10T20:15:30.893 に答える
9

気になる分野:

  • リソース アロケーター。手動のリソース管理では、リソースの割り当てにかかる時間を最小限に抑えるか、断片化を最小限に抑えることができます。ここでの各戦略には、同じインターフェースを持つ「割り当て」メソッドがあり、ユーザーは最適化しようとしているものに基づいて、どの戦略を使用するかを決定します。
  • ネットワーク データを接続して送信するための方法。場合によっては、UDP データグラムに接続して送信することを好むかもしれませんし、TCP/IP を使用して送信するよりもパフォーマンスが重要でない場合もあります。
  • データのフォーマット/シリアル化戦略。オブジェクトを Json でシリアル化するか Xml でシリアル化するかをコードで決定できるようにします。1 つは機械用で、もう 1 つは人間が読める状況用です。どちらの戦略にも、オブジェクトを受け取る「Serialize」メソッドがあります。シリアル化はそれぞれ異なります。

テーマは、何かを何らかの方法で行うか別の方法で行うかの決定は状況要因に依存し、ユーザーまたはコードは状況に基づいて正しい戦略を選択するということです。

これが次のようなものよりも便利なのはなぜでしょうか。

void DoIt()
{
    if (... situation1...)
    {
       DoA()
    }
    else
    {
       DoB();
    }
}

その理由は、一度決定を下して忘れてしまいたい場合があるからです。戦略パターンのもう 1 つの重要なテーマは、戦略を実行する必要があるコードから、どの戦略を使用するかについての決定を分離することです。

DoItStrategy MakeDoItStrategy()
{
     if (... situation1...)
     {
           return new DoItStrategyA();
     }
     else
     {
           return new DoItStrategyB();
     }
}

最後の例では、ストラテジーを格納し、ストラテジー インターフェイスを実装する別のオブジェクトとして渡すことができます。戦略を実行する人にとっては、アクションを実行する方法があるだけです。彼らは、内部の仕組みが何であるかを知りません。インターフェースが満足することだけを知っています。戦略のユーザーは、私たちが決定を下した理由を知る必要はありません。彼らはただ行動を起こす必要があります。一度決定を下し、戦略を使用するクラスに戦略を渡します。

たとえば、特定のネットワーク構成に基づいて、UDP を使用してリモート ホストに接続し、データを送信するというプログラム全体の決定を下す場合を考えてみましょう。ネットワーク インターフェイスの各ユーザーが決定を下すためのロジック (上記の「DoIt」関数) を知る必要がある代わりに、UDP 戦略を前もって作成し、ネットワーク データを送信する必要があるすべての人に渡すことができます。次に、この戦略は、同じ最終結果 (データが A から B に取得される) を持つ単純なインターフェイスを実装します。

于 2009-11-10T20:10:02.550 に答える
4

あなたが引用しているコードスニペットは、(わずかに)文脈から外れているという点で少し欺瞞的です。以下の例で書いていることは、戦略パターンでもあります。上記の例をもう少し簡潔に書き直しただけです。

この例の主なポイントは、数学演算の詳細が呼び出し元から離れて抽象化されていることです。このようにして、呼び出し元は、新しい ConcreteStrategy を作成することにより、任意の二項演算子を操作できます。

  int mod = new ConcreteStrategy(){ 
         public int execute(int a, int b){ return a %b; }
  }.execute(3,4);
于 2009-11-10T20:08:20.393 に答える
2

Mac と iPhone で使用される Cocoa フレームワークは、ストラテジー パターンを多く使用しますが、それをデリゲート パターンと呼んでいます。使用方法は次のとおりです。

という具体的なオブジェクトがありますNSTableView。テーブルビューは、行数、各行、各列に何が入るかなどを知る必要があります。そのため、テーブルビューをサブクラス化してその情報を提供する代わりに、「デリゲート」オブジェクトを提供します。そのデリゲート オブジェクトは、特定のインターフェイス (Objective-C の「プロトコル」) を実装します。次に、テーブルビューはデリゲート オブジェクトに、特定の状況で何をすべきかを単純に尋ねることができます (「何行ありますか?」「このセルには何が入りますか?」「ユーザーはこの行を選択できますか?」)。NSTableViewDelegateプロトコルに準拠する新しいオブジェクトを割り当てるだけで、実行時にデリゲート オブジェクトを交換できます。

そうですね、戦略パターンは私のお気に入りの 1 つであり、毎日使用しています。

于 2009-11-10T20:16:25.367 に答える
2

ええ、例は不十分ですが、さまざまな実装戦略を持つデリゲートの概念は、私が非常に多くのアプリで使用してきた基本的で非常に古い設計パターンです。

ここでの問題は一般的なものだと思いますが、私が今までに見たほとんどすべてのデザインパターンの例は信じられないほど不自然であり、常にあなたと同じように質問するようになります-このように提示されると、それらは常に役に立たないように見えます.

于 2009-11-10T20:11:56.453 に答える
1

私は通常、状況に応じて、さまざまなことを行う必要がある場合に戦略パターンを使用します。本質的には、長い一連の if/else ステートメントを数行に変換する方法です。

これを行う 1 つの方法 (Java で):

Map<String, Strategy> strategyMap = new HashMap<String, Strategy>();
strategyMap.put("bark", new BarkingStrategy());
strategyMap.put("meow", new MeowingStrategy());
strategyMap.put("moo", new MooingStrategy());
strategyMap.put("giraffeSound", new UnknownStrategy());

まず、何らかの形で戦略のレパートリーを構築します。

後で...

String command = //...some form of input
strategyMap.get(command).execute();

このようにして、さまざまな状況を「一般的に」処理できます。

すなわち:

moo

を実行しますMooingStrategy()

于 2009-11-10T20:10:02.690 に答える
1

戦略パターンは、ユーザー (またはコードのユーザー) がアルゴリズムの計算を変更したい場合に役立ちます。私が戦略パターンを使用した単純な例は、A* 検索でのヒューリスティックのモデル化です。A* はヒューリスティックを使用します。これは、ゴール ノード (Ng) へのパス上で特定のノード (Ni) が選択された場合の残りのコストを見積もるための簡単な計算です。私のインターフェースは次のようになりました:

class Heuristic {
public:
    virtual int estimateRemainingCost(const node &Ni, const node &Ng) const = 0;
};

そして、それらは次のように使用されます。

...
// for each node (Ni) that is adjacent to the current node Nc
int node_priority = cost(Ni)/* the cost of choosing this node on the path */
                    + heuristic->estimateRemainingCost(Ni, Ng);
unsearched_nodes_.queue(node_priority, Ni);
...

ヒューリスティックは、置き換えることができる戦略または計算です。言い換えれば、検索アルゴリズムのヒューリスティックを変更するのは簡単な作業です。

于 2009-11-10T20:44:27.107 に答える
0

主な違いは、2 番目の例では、戦略がアルゴリズムであることです (したがって、パターンはありません)。最初の例では、アルゴリズムの一部を抽象化/分離しています。

たとえば、の実装は次のContext.executeStrategy()ようになります。

public int executeStrategy(int baseValue, int exponentFactor)
{
    return (int) Math.Pow(baseValue, this.Strategy.Calculate(2, exponentFactor));
}
于 2009-11-10T20:17:58.173 に答える
0

Context、提供された何らかの操作が与えられた場合に、複雑なことを行う方法を知っています。演算は単純なものです (2 つの数値を加算する、2 つの数値を乗算するなど)。自明ではない例のAContextには、さまざまな動作 (1 つだけでなく) が必要になる場合があり、それらは "戦略" として分解するのが最適です (サブクラス化を試みると、サブクラスContextの組み合わせ爆発が発生するため)。

于 2009-11-10T20:11:03.483 に答える
0

コンテキストオブジェクトがより多くの責任を持ち、戦略の抽象化がこれらの責任を操作のいくつかの側面から切り離す場合、それはより理にかなっています。1 つの例 (C# の場合) は、IComparer インターフェイスです。

interface IComparer<T>
{
    int Compare(T a, T b);
}

これは、ソートアルゴリズムに渡すことができます。

于 2009-11-10T20:12:14.317 に答える