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