テンプレートメソッドパターンと戦略パターンの違いは何ですか?
私が知る限り、それらは 99% 同じです。唯一の違いは、テンプレート メソッド パターンが基本クラスとして抽象クラスを持っているのに対し、戦略クラスは各具象戦略クラスによって実装されるインターフェイスを使用することです。
しかし、クライアントに関する限り、それらはまったく同じ方法で消費されます - これは正しいですか?
テンプレートメソッドパターンと戦略パターンの違いは何ですか?
私が知る限り、それらは 99% 同じです。唯一の違いは、テンプレート メソッド パターンが基本クラスとして抽象クラスを持っているのに対し、戦略クラスは各具象戦略クラスによって実装されるインターフェイスを使用することです。
しかし、クライアントに関する限り、それらはまったく同じ方法で消費されます - これは正しいですか?
テンプレート パターンは、特定の操作に、他のさまざまなプリミティブ動作に関して定義できる不変の動作がある場合に使用されます。抽象クラスは不変の動作を定義し、実装クラスは依存メソッドを定義します。
戦略では、動作の実装は独立しています。各実装クラスが動作を定義し、それらの間で共有されるコードはありません。どちらも行動パターンであり、クライアントによってほぼ同じように消費されます。通常、戦略には 1 つのパブリック メソッド (execute()
メソッド) がありますが、テンプレートでは、一連のパブリック メソッドと、サブクラスが実装する必要があるサポート プライベート プリミティブのセットを定義できます。
2 つのパターンを簡単に組み合わせて使用できます。テンプレート パターンを使用して実装された戦略のファミリーに複数の実装が属する戦略パターンがあるとします。
2 つの主な違いは、具象アルゴリズムが選択されるタイミングです。
Template メソッド パターンでは、テンプレートをサブクラス化することにより、コンパイル時にこれが行われます。各サブクラスは、テンプレートの抽象メソッドを実装することにより、異なる具象アルゴリズムを提供します。クライアントがテンプレートの外部インターフェイスのメソッドを呼び出すと、テンプレートは、アルゴリズムを呼び出すために必要に応じて抽象メソッド (内部インターフェイス) を呼び出します。
class ConcreteAlgorithm : AbstractTemplate
{
void DoAlgorithm(int datum) {...}
}
class AbstractTemplate
{
void run(int datum) { DoAlgorithm(datum); }
virtual void DoAlgorithm() = 0; // abstract
}
対照的に、ストラテジー パターンでは、アルゴリズムを実行時に包含によって選択できます。具体的なアルゴリズムは、ストラテジーのコンストラクターまたはセッター メソッドへのパラメーターとして渡される個別のクラスまたは関数によって実装されます。このパラメーターに選択されるアルゴリズムは、プログラムの状態または入力に基づいて動的に変化する可能性があります。
class ConcreteAlgorithm : IAlgorithm
{
void DoAlgorithm(int datum) {...}
}
class Strategy
{
Strategy(IAlgorithm algo) {...}
void run(int datum) { this->algo.DoAlgorithm(datum); }
}
両方のパターンのクラス図は違いを示していると思います。
戦略
アルゴリズムをクラス内にカプセル化する
画像へのリンク
テンプレート メソッド
アルゴリズムの正確なステップをサブクラス
に委ねる 画像へのリンク
おそらくテンプレートメソッドパターンを意味します。そうです、彼らは非常によく似たニーズに応えます。サブクラスがこれらのステップをオーバーライドして詳細を変更するステップを定義した「テンプレート」アルゴリズムがある場合は、テンプレートメソッドを使用することをお勧めします。戦略の場合、インターフェイスを作成する必要があり、継承の代わりに委任を使用しています。私はそれがもう少し強力なパターンであり、DIP - 依存関係の逆転の原則に従ってより良いかもしれないと思います。テンプレートメソッドには適用されない、何かを行う方法である戦略の新しい抽象化を明確に定義するため、より強力です。したがって、この抽象化が理にかなっている場合は、それを使用してください。ただし、テンプレート法を使用すると、単純なケースではより単純なデザインが得られる場合があり、これも重要です。どの単語がより適切かを検討してください。テンプレートアルゴリズムはありますか?それとも、ここで重要なことは、戦略の抽象化、つまり何かを行う新しい方法を持っていることです
テンプレート メソッドの例:
Application.main()
{
Init();
Run();
Done();
}
ここでは、アプリケーションから継承し、init、run、および done で正確に行われることを置き換えます。
戦略の例:
array.sort (IComparer<T> comparer)
ここで、比較子を作成するときは、配列から継承しません。Array は、比較アルゴリズムを比較子に委譲します。
Strategy と Template メソッドのパターンには、多くの類似点があります。戦略とテンプレートの両方のメソッド パターンを使用して、Open-Closed Principle を満たし、コードを変更せずにソフトウェア モジュールを簡単に拡張できます。どちらのパターンも、一般的な機能をその機能の詳細な実装から分離することを表しています。ただし、提供する粒度の点で少し異なります。
これら 2 つのパターンを調べているときに私が観察した違いの一部を次に示します。
画像はバイトサイズのブログから取得しました。
継承と集約 (is-a と has-a)。同じ目標を達成するための 2 つの方法です。
この質問は、選択肢間のトレードオフのいくつかを示しています:継承と集約
どちらも非常に似ており、どちらもクライアント コードによって同様の方法で使用されます。上記の最も一般的な回答とは異なり、どちらも実行時にアルゴリズムを選択できます。
2 つの違いは、戦略パターンでは、異なる実装で目的の結果を達成するためのまったく異なる方法を使用できるのに対し、テンプレート メソッド パターンでは、結果を達成するために使用される包括的なアルゴリズム (「テンプレート」メソッド) を指定することです。 - 特定の実装 (サブクラス) に残された唯一の選択肢は、前述のテンプレート メソッドの特定の詳細です。これは、それ自体が抽象的ではなく、サブクラスによってオーバーライドされないテンプレート メソッドとは異なり、サブクラスによってオーバーライドされる (つまり、実装される) 1 つ以上の抽象メソッドをテンプレート メソッドに呼び出させることによって行われます。 .
クライアント コードは、ストラテジー パターンの使用時と同様に、実行時に決定できる具体的なサブ クラスの 1 つのインスタンスを指す抽象クラス タイプの参照/ポインターを使用して、テンプレート メソッドを呼び出します。
戦略パターンでは、サブクラスがショーを実行し、アルゴリズムを制御します。ここで、コードはサブクラス間で複製されます。アルゴリズムの知識とその実装方法は、多くのクラスに分散されています。
テンプレートパターンでは、基本クラスにアルゴリズムがあります。サブクラス間の再利用を最大化します。アルゴリズムは 1 か所にあるため、基底クラスがそれを保護します。
いいえ、必ずしも同じように消費されるとは限りません。「テンプレート メソッド」パターンは、将来の実装者に「ガイダンス」を提供する方法です。あなたは彼らに、「すべての Person オブジェクトには社会保障番号が必要です」と言っています (これは些細な例ですが、アイデアは正しく伝わります)。
戦略パターンにより、複数の可能な実装を切り替えることができます。(通常) 継承によって実装されるのではなく、呼び出し元に目的の実装を渡すことによって実装されます。例として、ShippingCalculator に税金を計算するいくつかの異なる方法 (NoSalesTax の実装や PercentageBasedSalesTax の実装など) のいずれかを提供できるようにすることが考えられます。
そのため、クライアントが実際にどの戦略を使用するかをオブジェクトに伝えることがあります。のように
myShippingCalculator.CalculateTaxes(myCaliforniaSalesTaxImpl);
しかし、テンプレート メソッドに基づくオブジェクトに対しては、クライアントは決してそれを行いません。実際、クライアントは、オブジェクトが Template Method に基づいていることさえ知らない場合があります。テンプレート メソッド パターン内のこれらの抽象メソッドは、保護されている可能性さえあります。その場合、クライアントはそれらが存在することさえ知りません。
テンプレート パターンは戦略パターンに似ています。これら 2 つのパターンは、範囲と方法論が異なります。
ストラテジーは、さまざまな種類の税の計算方法など、発信者がアルゴリズム全体を変更できるようにするために使用され、テンプレート メソッドはアルゴリズムのステップを変更するために使用されます。このため、Strategy はより粗い粒度になっています。テンプレートを使用すると、一連の操作でよりきめ細かい制御を行うことができますが、これらの詳細の実装を変更することもできます。
もう 1 つの主な違いは、Strategy は委譲を使用し、Template Method は継承を使用することです。ストラテジーでは、サブジェクトが参照する別の xxxStrategy クラスにアルゴリズムが委譲されますが、テンプレートでは、ベース メソッドとオーバーライド メソッドをサブクラス化して変更を加えます。
http://cyruscrypt.blogspot.com/2005/07/template-vs-strategy-patterns.htmlから
この設計パターンのテンプレート メソッドでは、1 つまたは複数のアルゴリズム ステップをサブクラスでオーバーライドして、包括的なアルゴリズムが引き続き使用されるようにしながら、異なる動作を許可できます (Wiki)。
パターン名 Template メソッドはそれが何であるかを意味します。CalculateSomething() メソッドがあり、このメソッドをテンプレート化するとします。このメソッドは、基本クラスで非仮想メソッドとして宣言されます。メソッドが次のようになっているとします。
CalculateSomething(){
int i = 0;
i = Step1(i);
i++;
if (i> 10) i = 5;
i = Step2(i);
return i;
Step1 および Step2 メソッドの実装は、派生クラスによって提供できます。
戦略パターンでは、ベースによって提供される実装はありません (これが、ベースが実際にクラス図のインターフェースである理由です)
古典的な例はソートです。ソートする必要があるオブジェクトの数に基づいて、適切なアルゴリズム クラス (マージ、バブル、クイックなど) が作成され、アルゴリズム全体が各クラスにカプセル化されます。
並べ替えをテンプレート メソッドとして実装できますか? 確かにできますが、抽象化して基本実装に配置する共通点はほとんどありません。したがって、テンプレート メソッド パターンの目的を無効にします。