web-dev-qa-db-ja.com

戦略パターンの利点

If/thenケースでコードを記述できるだけの場合に、戦略パターンを使用することが有益なのはなぜですか?

たとえば、TaxPayerクラスがあり、そのメソッドの1つがさまざまなアルゴリズムを使用して税金を計算しています。では、if/thenケースを使用して、戦略パターンを使用する代わりに、そのメソッドで使用するアルゴリズムを理解できないのはなぜですか?また、TaxPayerクラスのアルゴリズムごとに個別のメソッドを実装できないのはなぜですか?

また、実行時にアルゴリズムが変更されるとはどういう意味ですか?

16
Armon Safai

1つには、if/elseブロックの大きな塊は簡単にテストできない。新しい「ブランチ」ごとに別の実行パスが追加されるため、 循環的複雑度 が増加します。コードを徹底的にテストしたい場合は、すべての実行パスをカバーする必要があります。各条件で、少なくとも1つ以上のテストを書く必要があります(小さな集中的なテストを書くと仮定します)。一方、戦略を実装するクラスは通常、1つのパブリックメソッドしか公開しないため、テストが簡単です。

したがって、ネストされたif/elseを使用すると、コードの単一の部分に対して多くのテストが行​​われ、Strategyを使用すると、複数のより単純な戦略のそれぞれに対していくつかのテストが行​​われます。後者の場合、実行パスを見逃すのが難しくなるため、カバレッジが向上しやすくなります。

extensibilityについては、ユーザーが独自の動作を注入できるはずのフレームワークを作成しているとしましょう。たとえば、ある種の税計算フレームワークを作成し、さまざまな国の税システムをサポートしたいとします。それらすべてを実装するのではなく、特定の税金を計算する方法の実装を提供する機会をフレームワークユーザーに提供したいだけです。

これが戦略パターンです:

  • インターフェースを定義します。 TaxCalculation、そしてフレームワークはこのタイプのインスタンスを受け入れて税金を計算します
  • フレームワークのユーザーは、このインターフェイスを実装してフレームワークに渡すクラスを作成し、計算の一部を実行する方法を提供します

フレームワークのコードを変更する必要があるため、if/elseを使用して同じことを行うことはできません。その場合、フレームワークではなくなります。フレームワークはコンパイルされた形式で配布されることが多いため、これが唯一のオプションとなる場合があります。

それでも、通常のコードをいくつか記述しただけでも、意図が明確になるため、戦略は有益です。 「このロジックはプラグイン可能で条件付き」とあります。つまり、ユーザーのアクション、構成、またはプラットフォームによって異なる複数の実装が存在する可能性があります。

戦略パターンを使用すると、読みやすさが向上する可能性があります。これは、特定の戦略を実装するクラスは通常、説明的な名前を持つ必要があるためです。 USAIncomeTaxCalculatorif/elseブロックは「名前なし」であり、最良の場合はコメントのみで、コメントは嘘をつきます。また、私の個人的な好みからすると、3つ以上のif/elseブロックが連続して存在することは読みにくく、ネストされたブロックではかなり悪くなります。

Open/Closed主義 も非常に関連があります。これは、上記の例で説明したように、Strategyはロジックをいくつかの部分で拡張できるためです。あなたのコード(「拡張のためにオープン」)を、それらの部分を書き直さずに(「変更のためにクローズ」)。

21
scriptin

If/thenケースでコードを記述できるだけの場合に、戦略パターンを使用することが有益なのはなぜですか?

場合によっては、if/thenを使用する必要があります。読みやすいシンプルなコードです。

単純なif/thenコードの2つの重要な問題は、 オープンクローズの原則 に違反する可能性があることです。条件を追加または変更する必要がある場合は、このコードを変更しています。より多くの条件があることが予想される場合、新しい戦略を追加するだけの方が簡単/クリーン/壊れそうにありません。

もう1つの問題は結合です。 if/thenを使用すると、すべての実装がその実装に関連付けられ、将来の変更が困難になります。戦略を使用することにより、唯一の結合は戦略のインターフェースへの結合です。

5
Telastyn

if/then条件は、 http://www.refactoring.com/catalog/replaceConditionalWithPolymorphism.html で説明されているように、typesに基づいています。

型チェックの条件文は通常、循環的複雑度がそれほど高くないので、Strategyが必ずしもそこにあるものを改善するとは言いません。

戦略の主な理由は、パターンを紹介したGoFブックp.316で説明されています。

戦略パターンは次の場合に使用します

...

  • クラスは多くの動作を定義し、これらはその操作で複数の条件ステートメントとして表示されます。多くの条件文の代わりに、関連する条件分岐を独自の戦略クラスに移動します。

他の回答で述べたように、戦略パターンを適切に適用すると、コードの残りの部分を変更する必要なく、新しい拡張(コンクリート戦略)を追加できます。これは、いわゆる Open-Closed原理 または Protected Variations原理 です。もちろん、新しい具体的な戦略をコーディングする必要があり、クライアントコードは戦略をプラグインとして認識する必要があります(これは簡単なことではありません)。

if/then条件付き。条件付きロジックを含むクラスのコードを変更する必要があります。他の回答で述べたように、再コンパイルせずに新しい機能(プラグイン)の追加をサポートするために複雑さを加えたくない場合は、これで問題ない場合があります。

4
Fuhrmanator

[...] if/thenケースでコードを記述するだけの場合

それがまさに戦略的パターンの最大の利点です。条件がありません。

クラス/メソッド/関数をできるだけシンプルで短くしたいとします。短いコードはテストが非常に簡単で、非常に読みやすいです。

条件(if/elseif/else)は、クラス/メソッド/関数を長くします。通常、1つの決定がtrueと評価されるコードは、決定がfalseと評価される部分。


戦略パターンのもう1つの大きな利点は、プロジェクト全体で再利用できることです。

戦略設計パターンを使用する場合、おそらくIoCコンテナの一種があり、そこからおそらくgetById(int id)メソッドによってインターフェイスの望ましい実装を取得します。ここで、idは列挙子メンバーである可能性があります。

つまり、実装の作成は、コードの1か所だけです。

さらに実装を追加したい場合は、getByIdメソッドに新しい実装を追加します。この変更は、それを呼び出すコードのすべての場所に反映されます。

if/elseif/elseでは、これは不可能です。新しい実装を追加することにより、新しいelseifブロックを追加して、実装が使用されたすべての場所でそれを実行する必要があります。構造。


また、実行時にアルゴリズムが変更されるとはどういう意味ですか?

私の例では、idは、ユーザー入力に基づいて入力される変数である可能性があります。ユーザーがボタンAをクリックすると、id = 2、ボタンBをクリックすると、id = 8

id値が異なるため、IoCコンテナーから取得されるインターフェイスの実装は異なり、コードは異なる操作を実行します。

3
Andy

If/thenケースでコードを記述できるだけの場合に、戦略パターンを使用することが有益なのはなぜですか?

戦略パターンを使用すると、アルゴリズム(詳細)をビジネスロジック(高レベルポリシー)から分離できます。これら2つは、混合すると読むのが難しいだけでなく、変更する理由が非常に異なります。

ここには、主要なチームワークのスケーラビリティ要素もあります。多くの人々がこの会計パッケージに取り組んでいる大規模なプログラミングチームを想像してみてください。税アルゴリズムがすべてTaxPayerクラスまたはモジュールにある場合、マージの競合が発生する可能性があります。マージの競合は時間がかかり、エラーが発生しやすくなります。今回は、チームの生産性を低下させ、悪いマージによって導入されたエラーは顧客との信頼性を損ないます。

また、実行時にアルゴリズムが変更されるとはどういう意味ですか?

実行時に変更されるアルゴリズムは、その動作が構成またはコンテキストによって決定されるアルゴリズムです。 if/thenアプローチを採用しても、アクティブに使用されている既存のクラスの再読み込みが含まれるため、これを効果的に有効にすることはできません。戦略パターンを使用すると、各アルゴリズムを実装する戦略オブジェクトを使用時に構築できます。その結果、これらのアルゴリズムに対する変更(バグ修正または拡張)が実行時に行われ、再ロードされる可能性があります。このアプローチは、継続的な可用性とゼロダウンタイムリリースを可能にするために使用できます。

2
Alain O'Dea

if/else自体には何の問題もありません。多くの場合、if/elseはロジックを表現する最も簡単で最も読みやすい方法です。したがって、あなたが説明するアプローチは、多くの場合完全に有効です。 (これも完全にテスト可能であるため、問題ありません。)

ただし、戦略パターンによってコード全体の保守性が向上する場合があります。例えば:

  • 特定の税計算アルゴリズムが相互に、およびコアロジックとは独立して変更される場合。この場合、変更がローカライズされるので、それらを別個のクラスに分けておくと便利です。
  • コアロジックを変更せずに、新しいアルゴリズムが将来追加される可能性がある場合。
  • 2つのアルゴリズムの違いの原因がコードの他の部分にも影響を与える場合。納税者の所得区分に基づいて、2つのアルゴリズムから選択するとします。この収入ブラケットによってコード内の他の場所でも別のブランチが選択される場合は、コードに複数のif/elseブランチを分散させるのではなく、収入ブラケットに対応する戦略を1回インスタンス化し、必要に応じて呼び出す方がきれいです。

戦略パターンが意味を持つためには、コアロジックと税計算アルゴリズムの間のインターフェースは、個々のコンポーネントよりも安定性が高いである必要があります。要件の変更によりインターフェースが変更される可能性が高い場合、戦略パターンは実際には責任となります。

「税金計算アルゴリズム」を、それを呼び出すコアロジックから明確に分離できるかどうかがすべてです。戦略パターンはif/elseに比べてオーバーヘッドがあるため、投資に価値があるかどうかはケースバイケースで決定する必要があります。

1
JacquesB