if / thenの場合にコードを書くことができるのに、なぜ戦略パターンを使用するのが有益なのですか?
例:TaxPayerクラスがあり、そのメソッドの1つが異なるアルゴリズムを使用して税金を計算します。では、なぜif / thenケースを持ち、戦略パターンを使用する代わりに、そのメソッドで使用するアルゴリズムを見つけられないのでしょうか?また、なぜTaxPayerクラスの各アルゴリズムに個別のメソッドを実装できないのですか?
また、実行時にアルゴリズムが変更されるとはどういう意味ですか?
if / thenの場合にコードを書くことができるのに、なぜ戦略パターンを使用するのが有益なのですか?
例:TaxPayerクラスがあり、そのメソッドの1つが異なるアルゴリズムを使用して税金を計算します。では、なぜif / thenケースを持ち、戦略パターンを使用する代わりに、そのメソッドで使用するアルゴリズムを見つけられないのでしょうか?また、なぜTaxPayerクラスの各アルゴリズムに個別のメソッドを実装できないのですか?
また、実行時にアルゴリズムが変更されるとはどういう意味ですか?
回答:
一つには、大きなif/else
ブロックの塊は簡単にテストできません。それぞれの新しい「ブランチ」は、他の実行パスを追加しますので、増加循環的複雑度を。コードを徹底的にテストする場合は、すべての実行パスをカバーする必要があり、各条件では少なくとも1つ以上のテストを書く必要があります(小規模で焦点を絞ったテストを書くことを想定しています)。一方、戦略を実装するクラスは、通常、テストが簡単な1つのパブリックメソッドのみを公開します。
したがって、ネストif/else
を使用すると、コードの単一の部分に対して多くのテストが行われますが、ストラテジーを使用すると、複数の単純なストラテジーのそれぞれに対してテストがほとんど行われなくなります。後者を使用すると、実行パスを見逃しにくいため、より良いカバレッジを簡単に作成できます。
拡張性については、ユーザーが独自の動作を注入できるフレームワークを書いていると想像してください。たとえば、ある種の税計算フレームワークを作成し、さまざまな国の税システムをサポートする必要があるとします。それらすべてを実装する代わりに、フレームワークのユーザーに特定の税金を計算する方法の実装を提供する機会を与えたいだけです。
戦略パターンは次のとおりです。
TaxCalculation
、インターフェースを定義し、フレームワークはこのタイプのインスタンスを受け入れて税を計算しますif/else
フレームワークのコードを変更する必要があるため、同じことはできません。その場合、フレームワークではなくなります。フレームワークは多くの場合コンパイルされた形式で配布されるため、これが唯一のオプションである可能性があります。
それでも、通常のコードを記述しただけでも、Strategyは意図を明確にするので有益です。「このロジックはプラガブルで条件付きです」、つまり、ユーザーのアクション、構成、またはプラットフォームに応じて異なる複数の実装が存在する可能性があります。
戦略パターンを使用すると読みやすさが向上する場合があります。特定の戦略を実装するクラスは、通常、説明的な名前を持つ必要があります。たとえばUSAIncomeTaxCalculator
、if/else
ブロックは「名前なし」です。また、私の個人的な好みから言うと、3 if/else
ブロック以上連続しているだけでは読みにくく、ネストされたブロックではかなり悪くなります。
オープン/クローズの原則は、私が上記の例で説明したように、戦略は、あなたがそれらの部分を書き換えることなく、あなたのコードの一部では、論理(「拡張のためのオープン」)を拡張することができます(「変更のため閉鎖」、ので、また、非常に関連性があります)。
if/else
ブロックはコードの可読性も低下させます。戦略パターンに関しては、オープン/クローズの原則はIMOに言及する価値があります。
if
ば多いほど、コードを通るパスが多くなり、より多くのテストを記述しなければならず、そのメソッドが失敗する方法が増えます。故ヨギベラの言葉を引用するなら、「もしあなたが道の分岐点に来たら、それを取りなさい」。これは単体テストに見事に当てはまります。さらに、多くのif
ステートメントは、これらの条件に対してロジックを繰り返す可能性が高いことを意味し、テスト負荷がさらに増加し、バグが発生するリスクが増加します。
if/else
それらを呼び出すために(またはそれらの内部で、それが何かをするべきかどうかを判断するために)たくさんのブロックが必要になるので、おそらくより読みやすいコードを除いて、あまり助けにはなりません。また、仮想フレームワークのユーザーには拡張性もありません。
if / thenの場合にコードを書くことができるのに、なぜ戦略パターンを使用するのが有益なのですか?
場合によっては、if / thenを使用する必要があります。読みやすいシンプルなコードです。
単純なif / thenコードの2つの重要な問題は、オープンクローズド原則に違反する可能性があることです。条件を追加または変更して変更する必要がある場合、このコードを変更しています。より多くの条件が必要な場合は、新しい戦略を追加するだけで、よりシンプル/クリーナー/破損の可能性が低くなります。
もう1つの問題はカップリングです。if / thenを使用することにより、すべての実装がその実装に結び付けられ、将来の変更が難しくなります。戦略を使用することで、唯一のカップリングは戦略のインターフェースになります。
条件がタイプに基づいている場合に戦略が役立ちif/then
ます、http://www.refactoring.com/catalog/replaceConditionalWithPolymorphism.htmlで説明されているようにます
型チェック条件は通常、循環的複雑性が高くないので、Strategyが必ずしも物事を改善するとは言いません。
戦略の主な理由は、パターンを紹介したGoF本p.316で説明されています。
戦略パターンを使用するのは
...
- クラスは多くの振る舞いを定義し、それらはその操作において複数の条件文として現れます。多くの条件の代わりに、関連する条件分岐を独自の戦略クラスに移動します。
他の回答で述べたように、適切に適用された場合、戦略パターンは、コードの残りの変更を必ずしも必要とせずに、新しい拡張機能(具体的な戦略)を追加できます。これは、いわゆるオープンクローズの原則または保護されたバリエーションの原則です。もちろん、新しい具体的な戦略をコーディングする必要があり、クライアントコードは戦略をプラグインとして認識する必要があります(これは簡単なことではありません)。
if/then
条件分岐、条件ロジックを含むクラスのコードを変更する必要があります。他の回答で述べたように、再コンパイルせずに新しい機能(プラグイン)の追加をサポートするために複雑さを追加したくない場合は、これで問題ない場合があります。
[...] if / thenの場合にコードを書くことができたら?
それはまさに戦略的パターンの最大の利点です。条件がありません。
クラス/メソッド/関数をできるだけシンプルかつ短くする必要があります。短いコードはテストが非常に簡単で読みやすいです。
通常、1つの決定が評価されるコードは決定が評価される部分と異なるため、条件(if
/ elseif
/ else
)はクラス/メソッド/関数を長くtrue
しfalse
ます。
戦略パターンのもう1つの大きな利点は、プロジェクト全体で再利用できることです。
戦略設計パターンを使用する場合、何らかのIoCコンテナーを使用する可能性が非常に高く、そこから、おそらく、 getById(int id)
メソッドid
。
つまり、実装の作成はコードの1か所でのみ行われます。
さらに実装を追加する場合は、新しい実装を getById
メソッドにこの変更は、呼び出したコードのすべての場所に反映されます。
if
/ elseif
/ else
これを行うのは不可能です。新しい実装を追加するには、新しいelseif
ブロックを追加して、実装が使用されたすべての場所でそれを行う必要があります。そうしないと、実装を構造に追加するのを忘れたため、無効なコードになる可能性があります。
また、実行時にアルゴリズムが変更されるとはどういう意味ですか?
私の例では、id
はユーザー入力に基づいて入力される変数です。ユーザーがボタンAをクリックすると、id = 2
、彼はその後、ボタンBをクリックした場合id = 8
。
id
値が異なるため、インターフェイスの異なる実装がIoCコンテナから取得され、コードは異なる操作を実行します。
if
/ elseif
/ else
状態。前と同じ、ただ別の場所に。
id
内の変数にスイッチがありgetById
、特定の実装が返されます。インターフェイスの実装が必要になるたびに、IoCコンテナにそれを配信するように依頼します。
getSortByEnumType(SortEnum type)
実装を返すSort
メソッドgetSortType
、SortEnum
変数を返すメソッド、およびコレクションをパラメーターとして取得するgetSortByEnumType
メソッドを使用することもできます。このメソッドには、type
パラメーターのスイッチが含まれ、正しい並べ替えアルゴリズムが返されます。新しいソートアルゴリズムを追加する必要がある場合は、enumと1つのメソッドを編集するだけで済みます。そして、あなたは設定されています。
if / thenの場合にコードを書くことができるのに、なぜ戦略パターンを使用するのが有益なのですか?
戦略パターンを使用すると、ビジネスロジック(高レベルポリシー)からアルゴリズム(詳細)を分離できます。これら2つのことは、混在した場合に読むのが混乱するだけでなく、変更する理由がまったく異なります。
ここには、チームワークの主要なスケーラビリティ要因もあります。多くの人がこの会計パッケージに取り組んでいる大規模なプログラミングチームを想像してください。税アルゴリズムがすべてTaxPayerクラスまたはモジュールにある場合、マージの競合が発生する可能性が高くなります。マージの競合は時間がかかり、エラーを解決する傾向があります。この時間はチームの生産性を低下させ、不良マージによって導入されたエラーは顧客との信頼性を損ないます。
また、実行時にアルゴリズムが変更されるとはどういう意味ですか?
実行時に変更されるアルゴリズムは、その動作が構成またはコンテキストによって決定されるものです。既存のif / thenアプローチでは、アクティブに使用されている既存のクラスをリロードする必要があるため、これを効果的に有効にすることはできません。Strategy Patternを使用すると、使用時に各アルゴリズムを実装する戦略オブジェクトを構築できます。その結果、これらのアルゴリズムの変更(バグ修正または機能強化)が実行時に行われ、再ロードされる可能性があります。このアプローチは、継続的な可用性とゼロダウンタイムのリリースを可能にするために使用できます。
if/else
それ自体に問題はありません。多くの場合if/else
、ロジックを表現する最も簡単で読みやすい方法です。したがって、あなたが説明するアプローチは多くの場合完全に有効です。(これも完全にテスト可能であるため、問題ではありません。)
ただし、戦略パターンによってコード全体の保守性が向上する場合があります。例えば:
戦略パターンが意味をなすためには、コアロジックと税計算アルゴリズムの間のインターフェイスがより安定している必要があります個々のコンポーネントよりもます。要件の変更によりインターフェースが変更される可能性が高い場合、戦略パターンは実際には不利になる可能性があります。
「税計算アルゴリズム」を、それを呼び出すコアロジックから明確に分離できるかどうかにかかっています。戦略パターンにはに比べてある程度のオーバーヘッドif/else
があるため、投資に見合う価値があるかどうかをケースバイケースで決定する必要があります。