テンプレートメソッドパターンと戦略パターンの違いは何ですか?
私が知る限り、それらは99%同一です。唯一の違いは、テンプレートメソッドパターンが基本クラスとして抽象クラスを持っているのに対し、戦略クラスは各具象戦略クラスによって実装されるインターフェースを使用することです。
ただし、クライアントに関する限り、それらはまったく同じ方法で消費されます-これは正しいですか?
テンプレートメソッドパターンと戦略パターンの違いは何ですか?
私が知る限り、それらは99%同一です。唯一の違いは、テンプレートメソッドパターンが基本クラスとして抽象クラスを持っているのに対し、戦略クラスは各具象戦略クラスによって実装されるインターフェースを使用することです。
ただし、クライアントに関する限り、それらはまったく同じ方法で消費されます-これは正しいですか?
回答:
2つの主な違いは、具体的なアルゴリズムが選択されるときです。
でテンプレートメソッドパターン、これはで起こるコンパイル時でサブクラス化テンプレートを。各サブクラスは、テンプレートの抽象メソッドを実装することにより、異なる具体的なアルゴリズムを提供します。クライアントがテンプレートの外部インターフェースのメソッドを呼び出すと、テンプレートは、アルゴリズムを呼び出すために必要なその抽象メソッド(その内部インターフェース)を呼び出します。
class ConcreteAlgorithm : AbstractTemplate
{
void DoAlgorithm(int datum) {...}
}
class AbstractTemplate
{
void run(int datum) { DoAlgorithm(datum); }
virtual void DoAlgorithm() = 0; // abstract
}
対照的に、Strategyパターンでは、包含によってアルゴリズムを実行時に選択できます。具体的なアルゴリズムは、コンストラクターまたはセッターメソッドへのパラメーターとして戦略に渡される個別のクラスまたは関数によって実装されます。このパラメーターに対してどのアルゴリズムが選択されるかは、プログラムの状態または入力に基づいて動的に変化する可能性があります。
class ConcreteAlgorithm : IAlgorithm
{
void DoAlgorithm(int datum) {...}
}
class Strategy
{
Strategy(IAlgorithm algo) {...}
void run(int datum) { this->algo.DoAlgorithm(datum); }
}
if (config.useAlgoA) impl = new AlgoA() else impl = new AlgoB()
)、この答えは正しくありません。
new ConcreteAlgorithm1()
対new ConcreteAlgorithm2()
。当然、選択は実行時に行われます(コンパイル時にアルゴリズムを選択すると、ハードコーディングすることになります)。2つの主な違いは、具体的なアルゴリズムの実装方法です。サブクラスとして実装されていますか、それとも個別のインターフェースとして実装されていますか?前者はテンプレートです。後者は戦略です。違いは、GoFブックの共通のテーマである、構成と継承とで要約できます。
テンプレートパターンは、特定の操作に、他のさまざまなプリミティブな動作で定義できる不変の動作がある場合に使用されます。抽象クラスは不変の動作を定義し、実装クラスは依存メソッドを定義しました。
戦略では、動作の実装は独立しています。各実装クラスが動作を定義し、それらの間で共有されるコードはありません。どちらも行動パターンであるため、クライアントはほとんど同じ方法で消費します。通常、戦略には単一のパブリックメソッド(execute()
メソッド)がありますが、テンプレートでは、パブリッククラスのセットと、サブクラスが実装する必要のあるサポートするプライベートプリミティブのセットを定義できます。
2つのパターンは簡単に一緒に使用できます。テンプレートパターンを使用して実装された戦略のファミリーにいくつかの実装が属する戦略パターンがある場合があります。
両方のパターンのクラス図が違いを示していると思います。
戦略
クラス内のアルゴリズムをカプセル化します
画像へのリンク
テンプレートメソッド
アルゴリズムの正確なステップをサブクラスLink to Imageに延期する
おそらくテンプレートメソッドパターンを意味します。あなたは正しい、彼らは非常に類似したニーズに対応しています。サブクラスがこれらのステップをオーバーライドして詳細を変更する定義済みのステップを持つ「テンプレート」アルゴリズムがある場合は、テンプレートメソッドを使用する方が良いと思います。戦略の場合は、インターフェースを作成する必要があり、継承の代わりに委任を使用します。私はそれがもう少し強力なパターンであり、おそらくDIP-依存関係の逆転の原則に準拠していると思います。戦略の新しい抽象化、つまりテンプレートメソッドには適用されない、何かを行う方法を明確に定義しているため、より強力です。したがって、この抽象化が理にかなっている場合は、それを使用してください。ただし、テンプレートメソッドを使用すると、単純なケースではより単純なデザインが得られる場合があり、これも重要です。どの単語の方が適しているかを検討します。テンプレートアルゴリズムはありますか?または、ここで重要なことは、戦略の抽象化-何かを行う新しい方法です。
テンプレートメソッドの例:
Application.main()
{
Init();
Run();
Done();
}
ここでは、アプリケーションから継承し、init、run、およびdoneで正確に何が行われるかを置き換えます。
戦略の例:
array.sort (IComparer<T> comparer)
ここでは、比較演算子を作成するときに、配列から継承しません。配列は比較アルゴリズムを比較子に委任します。
ストラテジーとテンプレートメソッドのパターンには、多くの類似点があります。ストラテジーとテンプレートの両方のメソッドパターンを使用して、開閉原理を満たし、ソフトウェアモジュールをコードを変更せずに簡単に拡張できます。どちらのパターンも、一般的な機能とその機能の詳細な実装の分離を表しています。ただし、提供する粒度の点で少し異なります。
これらの2つのパターンを調査しているときに私が観察した違いのいくつかを次に示します。
画像はバイト化されたブログから取得されます。
どちらも非常によく似ており、どちらもクライアントコードによって同様の方法で使用されます。上記の最も一般的な答えが言うこととは異なり、どちらも実行時にアルゴリズムを選択できます。
2つの違いは、戦略パターンでは異なる実装で目的の結果を達成するための完全に異なる方法を使用できる一方で、テンプレートメソッドパターンは結果を達成するために使用される包括的なアルゴリズム(「テンプレート」メソッド)を指定することです。 -特定の実装(サブクラス)に残された唯一の選択肢は、上記のテンプレートメソッドの特定の詳細です。これは、テンプレートメソッドで1つ以上の抽象を呼び出すことによって行われます。、テンプレートメソッド自体が抽象的でなく、サブクラスによってオーバーライドされないテンプレートメソッドとは異なり、テンプレートメソッドがサブクラスによってオーバーライドされる(つまり実装される)メソッドをます。 。
クライアントコードは、ストラテジーパターンを使用するときと同じように実行時に決定できる具象サブクラスの1つのインスタンスを指す抽象クラスタイプの参照/ポインターを使用して、テンプレートメソッドを呼び出します。
テンプレートメソッド:
戦略:
戦略構造:
理解を深めるために、テンプレートメソッドと戦略の記事をご覧ください。
関連記事:
いいえ、必ずしも同じように消費されるわけではありません。「テンプレートメソッド」パターンは、将来の実装者に「ガイダンス」を提供する方法です。「すべてのPersonオブジェクトには社会保障番号が必要です」と言っています(これは簡単な例ですが、正しく理解できます)。
戦略パターンでは、複数の可能な実装を切り替えることができます。これは(通常は)継承によって実装されるのではなく、呼び出し側に目的の実装を渡させることによって実装されます。例としては、ShippingCalculatorに税金を計算するいくつかの異なる方法(NoSalesTax実装、およびPercentageBasedSalesTax実装)の1つを提供できるようにすることが考えられます。
そのため、クライアントは実際にどの戦略を使用するかをオブジェクトに伝えます。のように
myShippingCalculator.CalculateTaxes(myCaliforniaSalesTaxImpl);
しかし、クライアントは、テンプレートメソッドに基づいたオブジェクトに対してこれを行うことはありません。実際、クライアントは、オブジェクトがテンプレートメソッドに基づいていることさえ知らない場合があります。テンプレートメソッドパターンのこれらの抽象メソッドは保護されている場合もあり、その場合、クライアントはそれらが存在することさえ知りません。
この記事を読むことをお勧めします。実際のケースの違いを説明します。
記事からの引用
「実装クラスもテンプレートメソッドクラスに依存していることがわかります。アルゴリズムの一部のステップを変更したい場合は、この依存関係によりテンプレートメソッドが変更されます。反対側の戦略では、アルゴリズムが完全にカプセル化されます。実装により、アルゴリズムを完全に定義するクラスです。したがって、変更があった場合は、以前に作成したクラスのコードを変更する必要があります。これが、クラスを設計するための戦略を選択した主な理由でした。
テンプレートメソッドの1つの特徴は、テンプレートメソッドがアルゴリズムを制御することです。これは他の状況では良いことかもしれませんが、私の問題では、これがクラスの設計を制限していました。一方、戦略はアルゴリズムのステップを制御しないため、完全に異なる変換方法を追加できます。したがって、私の場合、戦略は実装に役立ちます。
戦略の1つの欠点は、コードの冗長性が高すぎ、コード共有が少ないことです。この記事の提示された例から明らかなように、4つのクラスで同じコードを何度も繰り返す必要があります。したがって、すべてに共通のステップ4などのシステムの実装が変更された場合、5つのクラスすべてでこれを更新する必要があるため、維持するのは困難です。一方、テンプレートメソッドでは、スーパークラスのみを変更でき、変更はサブクラスに反映されます。したがって、テンプレートメソッドは、クラス間で非常に低い冗長性と大量のコード共有を提供します。
戦略では、実行時にアルゴリズムを変更することもできます。テンプレートメソッドでは、オブジェクトを再初期化する必要があります。この戦略の機能により、柔軟性が大幅に向上します。設計の観点からは、継承よりも構成を優先する必要があります。したがって、戦略パターンを使用することも、開発の主な選択肢となりました。」
テンプレートパターンは、戦略パターンに似ています。これら2つのパターンは、範囲と方法が異なります。
戦略は、呼び出し側がさまざまなタイプの税を計算する方法など、アルゴリズム全体を変更できるようにするために使用され、テンプレートメソッドは、アルゴリズムのステップを変更するために使用されます。このため、戦略はより粗くなります。テンプレートを使用すると、一連の操作でより細かい制御が可能になり、さらにこれらの詳細の実装を変えることができます。
その他の主な違いは、テンプレートメソッドが継承を使用するのに対して、戦略は委任を使用することです。ストラテジーでは、アルゴリズムはサブジェクトが参照する別のxxxStrategyクラスに委任されますが、テンプレートを使用すると、ベースメソッドとオーバーライドメソッドをサブクラス化して変更を加えます。
http://cyruscrypt.blogspot.com/2005/07/template-vs-strategy-patterns.htmlから
戦略デザインパターン
テンプレートメソッドデザインパターン
テンプレートメソッドは、基本クラスで定義されているアルゴリズムのメインの構造とステップを変更せずに、サブクラスがアルゴリズムの特定のステップを再定義できるようにすることです。テンプレートパターンは通常継承を使用するため、基本クラスでアルゴリズムの一般的な実装を提供できます。必要に応じて、サブクラスがオーバーライドすることを選択できます。
public abstract class RobotTemplate {
/* This method can be overridden by a subclass if required */
public void start() {
System.out.println("Starting....");
}
/* This method can be overridden by a subclass if required */
public void getParts() {
System.out.println("Getting parts....");
}
/* This method can be overridden by a subclass if required */
public void assemble() {
System.out.println("Assembling....");
}
/* This method can be overridden by a subclass if required */
public void test() {
System.out.println("Testing....");
}
/* This method can be overridden by a subclass if required */
public void stop() {
System.out.println("Stopping....");
}
/*
* Template algorithm method made up of multiple steps, whose structure and
* order of steps will not be changed by subclasses.
*/
public final void go() {
start();
getParts();
assemble();
test();
stop();
}
}
/* Concrete subclass overrides template step methods as required for its use */
public class CookieRobot extends RobotTemplate {
private String name;
public CookieRobot(String n) {
name = n;
}
@Override
public void getParts() {
System.out.println("Getting a flour and sugar....");
}
@Override
public void assemble() {
System.out.println("Baking a cookie....");
}
@Override
public void test() {
System.out.println("Crunching a cookie....");
}
public String getName() {
return name;
}
}
上記のコードでは、go()アルゴリズムのステップは常に同じですが、サブクラスは特定のステップを実行するための異なるレシピを定義している場合があります。
戦略パターンとは、クライアントが実行時に具体的なアルゴリズムの実装を選択できるようにすることです。すべてのアルゴリズムは分離され独立していますが、共通のインターフェースを実装しており、アルゴリズム内で特定のステップを定義するという概念はありません。
/**
* This Strategy interface is implemented by all concrete objects representing an
* algorithm(strategy), which lets us define a family of algorithms.
*/
public interface Logging {
void write(String message);
}
/**
* Concrete strategy class representing a particular algorithm.
*/
public class ConsoleLogging implements Logging {
@Override
public void write(String message) {
System.out.println(message);
}
}
/**
* Concrete strategy class representing a particular algorithm.
*/
public class FileLogging implements Logging {
private final File toWrite;
public FileLogging(final File toWrite) {
this.toWrite = toWrite;
}
@Override
public void write(String message) {
try {
final FileWriter fos = new FileWriter(toWrite);
fos.write(message);
fos.close();
} catch (IOException e) {
System.out.println(e);
}
}
}
完全なソースコードについては、私のgithub リポジトリを確認してください。
ストラテジーは、抽象クラスとしてインターフェースおよびテンプレートメソッドとして公開されます。これは通常、フレームワークでよく使用されます。たとえば、SpringフレームワークのMessageSourceクラスは、メッセージを解決するための戦略インターフェースです。クライアントは、このインターフェースの特定の実装(戦略)を使用します。
また、同じインターフェースAbstractMessageSourceの抽象実装。これには、メッセージ解決の共通実装があり、サブクラスがそれらの方法でそれらを実装できるようにresolveCode()抽象メソッドを公開します。AbstractMessageSourceはテンプレートメソッドの例です。
このデザインパターンのテンプレートメソッドでは、1つまたは複数のアルゴリズムステップをサブクラスでオーバーライドして、包括的なアルゴリズムを確実に実行しながら、異なる動作を許可できます(Wiki)。
パターン名テンプレートメソッドは、それが何であるかを意味します。メソッドCalculateSomething()があり、このメソッドをテンプレート化したいとします。このメソッドは、基本クラスで非仮想メソッドとして宣言されます。メソッドが次のようになっているとします。
CalculateSomething(){
int i = 0;
i = Step1(i);
i++;
if (i> 10) i = 5;
i = Step2(i);
return i;
Step1およびStep2メソッドの実装は、派生クラスによって提供できます。
戦略パターンでは、ベースによって提供される実装はありません(これが、ベースが実際にクラス図のインターフェースである理由です)
古典的な例はソートです。ソートする必要のあるオブジェクトの数に基づいて、適切なアルゴリズムクラス(マージ、バブル、クイックなど)が作成され、アルゴリズム全体が各クラスにカプセル化されます。
これで、並べ替えをテンプレートメソッドとして実装できますか?確かにできますが、抽象化して基本実装に配置する共通点はほとんどありません。そのため、テンプレートメソッドパターンの目的に反します。