15

次のコード サンプルは、Wikipedia からコピーした Strategy パターンの実装です。私の完全な質問はそれに続きます...

ウィキのmain方法:

//StrategyExample test application

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);

    }

}

パターンピース:

// The classes that implement a concrete strategy should implement this

// The context class uses this to call the concrete strategy
interface Strategy {

    int execute(int a, int b);

}

// Implements the algorithm using the strategy interface
class ConcreteStrategyAdd implements Strategy {

    public int execute(int a, int b) {
        System.out.println("Called ConcreteStrategyA's execute()");
        return a + b;  // Do an addition with a and b
    }

}

class ConcreteStrategySubtract implements Strategy {

    public int execute(int a, int b) {
        System.out.println("Called ConcreteStrategyB's execute()");
        return a - b;  // Do a subtraction with a and b
    }

}

class ConcreteStrategyMultiply implements Strategy {

    public int execute(int a, int b) {
        System.out.println("Called ConcreteStrategyC's execute()");
        return a  * b;   // Do a multiplication with a and b
    }

}

// Configured with a ConcreteStrategy object and maintains a reference to a Strategy object
class Context {

    private Strategy strategy;

    // Constructor
    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    public int executeStrategy(int a, int b) {
        return strategy.execute(a, b);
    }

}

上記の例を具体的に考えると、Contextクラスは冗長ですか?

mainたとえば、 Context を除く既存のクラスとインターフェイスを使用して、次の代替実装を考え出すことができます。これはまったく同じように機能します。まだ疎結合です。

((編集:この単純なシナリオで、Context クラスを省略した場合、将来間違いを犯すことになりますか? ))

public static void main(String[] args) {

    IStrategy strategy;

    // Three strategies
    strategy = new ConcreteStrategyAdd();
    int resultA = strategy.executeStrategy(3,4);

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

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

}

まとめの更新

回答とコメントを通じて発見されたものをポイント形式でリストします。

  • Context は、合成された Strategy の使用方法 (呼び出しのタイミングなど) のバリエーションを可能にします。異なるコンテキストは、特定の戦略を呼び出す前と後に異なる内部作業を行う場合があります。
  • コンテキストは高レベルの「ブラック ボックス」です。クライアントはコンテキストの呼び出し方法しか理解できないため、クライアントを壊すことなく、コンテキスト ロジックを変更したり、合成された戦略を変更したり (または別のものを使用したり) することができます。
  • Context を省略してウィキペディアのサンプル コードの別の実装を作成しましたが、元のコードと同じように機能しましたが、状況全体が単純化され (どちらの場合も)、私の変更は実際には次のことを意味していました: 1. 戦略ではない2.ここで言及されている戦略パターンの精神の利点が恋しいです。
  • 私の別の実装では、Context のようなメイン メソッドを使用していたので、効果的にシミュレートする場合は、Context を保持した方がよいでしょう。不純な戦略パターンを作成することで、混乱が生じました。一からやり直す必要も、より賢くしようとする必要もありませんでした (この場合)。

他のポイントが役立つ場合、またはこれを修正する必要がある場合は、コメントを残してください。それに応じてリストを変更します.

4

4 に答える 4

16

名前が示すようにContext、戦略が実行されるポイントをカプセル化するものです。それがなければ、あなたは裸Strategyの を持っているだけで、呼び出し元のクラスは追加の責任を負います:Strategyそれ自体をいつ呼び出すかを知ることです. あなたの例はおそらく少し単純すぎます。この特定のケースでは、Contextあまり理解していないと思います。

a の有用性をよりよく示す例はContext、次のようなものです。

public class LoadingDock {   // Context.
  private LoadStrategy ls;   // Strategy.

  public void setLoadStrategy(LoadStrategy ls) { ... }

  // Clients of LoadingDock use this method to do the relevant work, rather
  // than taking the responsibility of invoking the Strategy themselves.
  public void shipItems(List<ShippingItem> l) {
    // verify each item is properly packaged     \
    // ...                                        |  This code is complex and shouldn't be
    // verify all addresses are correct           |  subsumed into consumers of LoadingDock.
    // ...                                        |  Using a Context here is a win because
    // load containers onto available vehicle     |  now clients don't need to know how a
    Vehicle v = VehiclePool.fetch();        //    |  LoadingDock works or when to use a
    ls.load(v, l);                          //   /   LoadStrategy.
  }
}

Strategyが外部クライアントから直接呼び出されることは決してないことに注意してください。ストラテジーのみshipItemsを使用し、その後のステップの詳細はブラック ボックスです。これにより、 はContextクライアントに影響を与えることなく戦略の使用方法を調整できます。たとえば、パフォーマンス目標やその他の目標を達成するために、手順を完全に並べ替えたり、調整 (または完全に削除) したりできますが、クライアントにとっては、 の外部インターフェイスはshipItems()まったく同じように見えます。

Contextまた、この例の は、内部状態に基づいていつでもLoadingDock変更できることに注意してください。LoadStrategyたとえば、ドックがいっぱいになると、より積極的なスケジューリング メカニズムに切り替わり、クレートがドックからトラックに迅速に積み込まれ、効率がいくらか犠牲になります (おそらく、トラックは以前ほど効率的に積み込まれません)。彼らはそうであったかもしれません)。

于 2010-01-06T02:58:45.530 に答える
4

これは、このシナリオで実際の " Context" クラスがどのように見えるかのより良い例です:

class Accumulator {
    private Strategy strategy; 

    public Accumulator(Strategy strategy) { 
        this.strategy = strategy; 
    } 

    public int accumulate(List<Integer> values) { 
        int result = values.get(0);
        for (int i = 1; i < values.size(); i++) {
           result = strategy.execute(result, values.get(i));
        }
        return result;
    } 
}

編集:コンストラクターのタイプミスが修正されました

于 2010-01-06T02:58:02.800 に答える
3

この作り話のためかもしれませんが、これをストラテジーのネプラスウルトラとは呼びません。

Context クラスは、インターフェイスの新しい具体的な実装を渡すだけで、クラスに異なる動作を与える方法を示しています。クラスはインターフェイスしか認識していないため、何も変更する必要はありません。それがポイントです。残りの例を文字通りに解釈しないでください。

コーディングした方法は機能しますが、重要なのは、これをメイン メソッドにダンプしたことです。これは、ストラテジーを通常使用する方法ではありません。クラス内でそれを行うことになりますが、Context はその簡単な例です。

于 2010-01-06T02:56:04.990 に答える
0

Contextパターンが重複することはなくStrategy、以下のシナリオで役立ちます。

  1. 特定の を呼び出すコードは、Strategyを呼び出さずに複数のクラスに分散されますContext。将来、インターフェースのリファクタリングや変更Strategyが必要になった場合、それは大変な作業になります。

  2. 特定の戦略を呼び出す前に、一部のデータを入力する必要があるとします。コンテキストは、追加情報を提供し、特定の戦略の戦略メソッドを呼び出すことで、ここに最適です。

    たとえばContext、Strategy と userId をパラメーターとして取得します。を実行する前にStrategyContextユーザー プロファイルに関連する多くの追加情報を提供する必要があります。Context必要な情報を取得し、戦略の戦略メソッドを実行します。Context がない場合、100 の異なる場所で戦略的メソッドを呼び出すと、100 の異なる場所でコードを複製する必要があります。

  3. Contextどの戦略を呼び出すかについて独立した決定を下すことができます。実行時の構成に応じて、戦略タイプを簡単に変更できます。ストラテジー コア USP は、関連するアルゴリズムのファミリーを切り替えています。コンテキストは、それを達成するための最良の場所です。

  4. 複数の戦略に基づいて行動する必要がある場合Contextは、最適な場所です。axtavt が提案する使用の回答Accumulatorは一例です。

詳細については、この投稿を参照してください。

戦略パターンの実例

于 2016-06-03T12:07:14.763 に答える