戦略パターンの利点


15

if / thenの場合にコードを書くことができるのに、なぜ戦略パターンを使用するのが有益なのですか?

例:TaxPayerクラスがあり、そのメソッドの1つが異なるアルゴリズムを使用して税金を計算します。では、なぜif / thenケースを持ち、戦略パターンを使用する代わりに、そのメソッドで使用するアルゴリズムを見つけられないのでしょうか?また、なぜTaxPayerクラスの各アルゴリズムに個別のメソッドを実装できないのですか?

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


2
これは宿題ですか?もしそうなら、前もって述べておいたほうがよいでしょう。
-Fuhrmanator

2
@Fuhrmanatorいいえ、それはありません
アーモンサファイ

回答:


20

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

したがって、ネストif/elseを使用すると、コードの単一の部分に対して多くのテストが行​​われますが、ストラテジーを使用すると、複数の単純なストラテジーのそれぞれに対してテストがほとんど行われなくなります。後者を使用すると、実行パスを見逃しにくいため、より良いカバレッジを簡単に作成できます。

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

戦略パターンは次のとおりです。

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

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

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

戦略パターンを使用すると読みやすさが向上する場合があります。特定の戦略を実装するクラスは、通常、説明的な名前を持つ必要があります。たとえばUSAIncomeTaxCalculatorif/elseブロックは「名前なし」です。また、私の個人的な好みから言うと、3 if/elseブロック以上連続しているだけでは読みにくく、ネストされたブロックではかなり悪くなります。

オープン/クローズの原則は、私が上記の例で説明したように、戦略は、あなたがそれらの部分を書き換えることなく、あなたのコードの一部では、論理(「拡張のためのオープン」)を拡張することができます(「変更のため閉鎖」、ので、また、非常に関連性があります)。


1
if/elseブロックはコードの可読性も低下させます。戦略パターンに関しては、オープン/クローズの原則はIMOに言及する価値があります。
マチェイチャワプク

1
テスト容易性が大きな理由です。(ほとんど)コード内のすべてのブランチをテストする必要があります。が多けれifば多いほど、コードを通るパスが多くなり、より多くのテストを記述しなければならず、そのメソッドが失敗する方法が増えます。故ヨギベラの言葉を引用するなら、「もしあなたが道の分岐点に来たら、それを取りなさい」。これは単体テストに見事に当てはまります。さらに、多くのifステートメントは、これらの条件に対してロジックを繰り返す可能性が高いことを意味し、テスト負荷がさらに増加し​​、バグが発生するリスクが増加します。
グレッグブルクハルト

答えてくれてありがとう。では、なぜ同じクラスの異なるアルゴリズムに別々のメソッドを使用できないのですか?
アーモンサファイ

あなたはできますが、if/elseそれらを呼び出すために(またはそれらの内部で、それが何かをするべきかどうかを判断するために)たくさんのブロックが必要になるので、おそらくより読みやすいコードを除いて、あまり助けにはなりません。また、仮想フレームワークのユーザーには拡張性もありません。
Scriptin

1
テストする方が簡単な理由を明確にできますか?ケースステートメント(またはif / then)をポリモーフィックメソッド(戦略の基礎)にリファクタリングする例は、テストが非常に簡単です。refactoring.com/catalog/replaceConditionalWithPolymorphism.htmlテストするすべての条件がわかっている場合は、それぞれのテストを作成します。戦略がある場合、それぞれに対してインスタンスを作成して実行する必要があります。戦略アプローチのテストはどのように簡単ですか?戦略にリファクタリングするとき、複雑なネストされたifについては話していません。
ファーマネーター

5

if / thenの場合にコードを書くことができるのに、なぜ戦略パターンを使用するのが有益なのですか?

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

単純なif / thenコードの2つの重要な問題は、オープンクローズド原則に違反する可能性があることです。条件を追加または変更して変更する必要がある場合、このコードを変更しています。より多くの条件が必要な場合は、新しい戦略を追加するだけで、よりシンプル/クリーナー/破損の可能性が低くなります。

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


if / thenコード内のコードの変更の何が問題になっていますか?アルゴリズムの1つがどのように機能するかを変更することにした場合、戦略パターンのコードも変更する必要はありませんか?
アーモンサファイ

@armonsafai-戦略を変更する場合は、戦略をテストするだけです。すべてのアルゴリズムを変更する場合、すべてのアルゴリズムをテストする必要があります。さらに悪いことに、新しい戦略を追加する場合は、戦略をテストするだけです。新しい条件を追加する場合、すべての条件をテストする必要があります。
テラスティン

4

条件がタイプに基づいている場合に戦略が役立ちif/thenますhttp://www.refactoring.com/catalog/replaceConditionalWithPolymorphism.htmlで説明されているようにます

型チェック条件は通常、循環的複雑性が高くないので、Strategyが必ずしも物事を改善するとは言いません。

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

戦略パターンを使用するのは

...

  • クラスは多くの振る舞いを定義し、それらはその操作において複数の条件文として現れます。多くの条件の代わりに、関連する条件分岐を独自の戦略クラスに移動します。

他の回答で述べたように、適切に適用された場合、戦略パターンは、コードの残りの変更を必ずしも必要とせずに、新しい拡張機能(具体的な戦略)を追加できます。これは、いわゆるオープンクローズの原則または保護されたバリエーションの原則です。もちろん、新しい具体的な戦略をコーディングする必要があり、クライアントコードは戦略をプラグインとして認識する必要があります(これは簡単なことではありません)。

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


3

[...] if / thenの場合にコードを書くことができたら?

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

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

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


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

戦略設計パターンを使用する場合、何らかのIoCコンテナーを使用する可能性が非常に高く、そこから、おそらく、 getById(int id)メソッドid

つまり、実装の作成はコードの1か所でのみ行われます。

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

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


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

私の例では、idはユーザー入力に基づいて入力される変数です。ユーザーがボタンAをクリックすると、id = 2、彼はその後、ボタンBをクリックした場合id = 8

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


答えてくれてありがとう。では、なぜ同じクラスの異なるアルゴリズムに別々のメソッドを使用できないのですか?
アーモンサファイ

@ArmonSafai別々のメソッドは本当に何かを解決するでしょうか?そうは思いません。問題をある場所から別の場所に移し、どのメソッドを呼び出すかは、条件の結果に基づいて決定されます。再び、if/ elseif/ else状態。前と同じ、ただ別の場所に。
アンディ

それでは、if / thenの場合が主な目的でしょうか?戦略パターンにもif / thenケースをメインで使用する必要はありませんか?
アーモンサファイ

1
@ArmonSafaiいいえ、そうではありません。メソッドid内の変数にスイッチがありgetById、特定の実装が返されます。インターフェイスの実装が必要になるたびに、IoCコンテナにそれを配信するように依頼します。
アンディ

1
@ArmonSafai インターフェースのgetSortByEnumType(SortEnum type)実装を返すSortメソッドgetSortTypeSortEnum変数を返すメソッド、およびコレクションをパラメーターとして取得するgetSortByEnumTypeメソッドを使用することもできます。このメソッドには、typeパラメーターのスイッチが含まれ、正しい並べ替えアルゴリズムが返されます。新しいソートアルゴリズムを追加する必要がある場合は、enumと1つのメソッドを編集するだけで済みます。そして、あなたは設定されています。
アンディ

2

if / thenの場合にコードを書くことができるのに、なぜ戦略パターンを使用するのが有益なのですか?

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

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

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

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


1

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

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

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

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.