テンプレート方式と戦略パターンの違いは何ですか?


161

テンプレートメソッドパターンと戦略パターンの違いは何ですか?

私が知る限り、それらは99%同一です。唯一の違いは、テンプレートメソッドパターンが基本クラスとして抽象クラスを持っているのに対し、戦略クラスは各具象戦略クラスによって実装されるインターフェースを使用することです。

ただし、クライアントに関する限り、それらはまったく同じ方法で消費されます-これは正しいですか?


2
:SOにおけるこの投稿は、同じ質問のためのより良い答えてい stackoverflow.com/questions/464524/...
Gob00st

12
gob00stにリンクされている質問は、戦略とブリッジの違いです。それはこの質問に対する答えではありません。
bluekeys 2015年

回答:


135

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); }
}

要約すれば:

  • テンプレートメソッドパターン:サブクラス化によるコンパイル時アルゴリズムの選択
  • 戦略パターン:包含による実行時アルゴリズムの選択

47
どちらのパターンも、使用されるアルゴリズムの実行時選択をサポートしているため(テンプレートメソッドの場合は、次のようになりますif (config.useAlgoA) impl = new AlgoA() else impl = new AlgoB())、この答えは正しくありません。
Borek Bernard、2010

13
もちろん可能ですが、テンプレートパターンは使用していません。実際、これがStrategyインスタンスを作成するコードがどのように見えるかとほぼ同じです!
家屋

21
-1、私はこの答え(完全に間違っているわけではありません)は、本当の違いがある点を見逃していると思います。@tvanfossonの答えははるかに優れています。
Doc Brown

1
@Karoly Nyisztor「動作を置き換える」ことも、「拡張ポイントを提供する」こともできます。何かが振る舞いであろうと拡張であろうと、それは実際には、与えられたパターンをどこに適用するかという文脈次第です。テンプレートメソッドパターンの各サブクラスを「戦略」と呼ぶこともできますし、戦略パターンの各戦略クラスを「拡張」と呼ぶこともできます。問題の事実は、この回答が述べている違いを除いて、彼らは同じことをするということです。これが正解です。
アンディ

1
具体的なアルゴリズムは、両方のパターンに対して同じ方法で選択されます。選択は、呼び出すことによって行われるnew ConcreteAlgorithm1()new ConcreteAlgorithm2()。当然、選択は実行時に行われます(コンパイル時にアルゴリズムを選択すると、ハードコーディングすることになります)。2つの主な違いは、具体的なアルゴリズムの実装方法です。サブクラスとして実装されていますか、それとも個別のインターフェースとして実装されていますか?前者はテンプレートです。後者は戦略です。違いは、GoFブックの共通のテーマである、構成と継承とで要約できます。
jaco0646

138

テンプレートパターンは、特定の操作に、他のさまざまなプリミティブな動作で定義できる不変の動作がある場合に使用されます。抽象クラスは不変の動作を定義し、実装クラスは依存メソッドを定義しました。

戦略では、動作の実装は独立しています。各実装クラスが動作を定義し、それらの間で共有されるコードはありません。どちらも行動パターンであるため、クライアントはほとんど同じ方法で消費します。通常、戦略には単一のパブリックメソッド(execute()メソッド)がありますが、テンプレートでは、パブリッククラスのセットと、サブクラスが実装する必要のあるサポートするプライベートプリミティブのセットを定義できます。

2つのパターンは簡単に一緒に使用できます。テンプレートパターンを使用して実装された戦略のファミリーにいくつかの実装が属する戦略パターンがある場合があります。


これは私には正しいように聞こえますが、なぜWikiPediaは「戦略パターンは実行時にアルゴリズムの動作を選択するためのものである」と述べているのですか?テンプレートメソッドと同様に、コンパイル時にアルゴリズムの動作を選択するためにも使用できますか?何か不足していますか?
BornToCode 2015

2
@BornToCode彼らが話していることは、実行時に特定の戦略を選択することだと思います。たとえば、方程式の根を数値で見つける方法はいくつかあります。問題のドメインまたはデータに応じて、方程式を解くためにNewton-Raphson、Euler、またはその他の戦略を選択できます。それらのそれぞれが戦略です。より大きなアルゴリズムは、方程式の解法がその一部であり、問​​題の品質に基づいて採用する戦略を選択します。
tvanfosson 2015

はい、しかしそれは戦略パターンがそのような場合にのみ使用されるべきではないのですか?コンパイル時にアルゴリズムの動作のみを選択する必要がある場合でも、引き続き戦略パターンを使用する必要があるのか​​、それともそのように使用するためのものではないのですか?
BornToCode 2015

1
@BornToCode私は戦略が選択が動的であるときに最も有用であると思います。テンプレートは基本的に、既知のさまざまな関連する動作を構築する方法です。使用するテンプレート化された動作を選択するために、いくつかの戦略を使用します(ただし、必ずしも戦略パターンではありません)。たとえば、製品の継承-基本製品を作成し、さまざまな製品の機能を追加します。インスタンス化する製品タイプ(クラス)の選択は、ロード元のテーブル/ビューによって異なる場合があります。戦略パターンは実際には機能しません。
tvanfosson 2015

2
@BornToCodeそれはどちらかまたは両方ではなく、そうです。適切なパターンを適用し、有用なパターンを組み合わせます。
tvanfosson 2015


24

おそらくテンプレートメソッドパターンを意味します。あなたは正しい、彼らは非常に類似したニーズに対応しています。サブクラスがこれらのステップをオーバーライドして詳細を変更する定義済みのステップを持つ「テンプレート」アルゴリズムがある場合は、テンプレートメソッドを使用する方が良いと思います。戦略の場合は、インターフェースを作成する必要があり、継承の代わりに委任を使用します。私はそれがもう少し強力なパターンであり、おそらくDIP-依存関係の逆転の原則に準拠していると思います。戦略の新しい抽象化、つまりテンプレートメソッドには適用されない、何かを行う方法を明確に定義しているため、より強力です。したがって、この抽象化が理にかなっている場合は、それを使用してください。ただし、テンプレートメソッドを使用すると、単純なケースではより単純なデザインが得られる場合があり、これも重要です。どの単語の方が適しているかを検討します。テンプレートアルゴリズムはありますか?または、ここで重要なことは、戦略の抽象化-何かを行う新しい方法です。

テンプレートメソッドの例:

Application.main()
{
Init();
Run();
Done();
}

ここでは、アプリケーションから継承し、init、run、およびdoneで正確に何が行われるかを置き換えます。

戦略の例:

array.sort (IComparer<T> comparer)

ここでは、比較演算子を作成するときに、配列から継承しません。配列は比較アルゴリズムを比較子に委任します。


3
これは素晴らしい答えだと思います
Calanus

23

戦略とテンプレートメソッドの違いパターン戦略とテンプレートメソッド


類似点

ストラテジーとテンプレートメソッドのパターンには、多くの類似点があります。ストラテジーとテンプレートの両方のメソッドパターンを使用して、開閉原理を満たし、ソフトウェアモジュールをコードを変更せずに簡単に拡張できます。どちらのパターンも、一般的な機能とその機能の詳細な実装の分離を表しています。ただし、提供する粒度の点で少し異なります。


違い

これらの2つのパターンを調査しているときに私が観察した違いのいくつかを次に示します。

  1. ストラテジーでは、クライアントとストラテジーの間の結合はより緩やかですが、テンプレートメソッドでは、2つのモジュールがより強く結合されます。
  2. ストラテジーでは、状況に応じて抽象クラスを使用することもできますが、ほとんどがインターフェースを使用し、具象クラスは使用しませんが、テンプレートメソッドでは、ほとんど抽象クラスまたは具象クラスを使用するため、インターフェースを使用しません。
  3. ストラテジーパターンでは、通常、クラスの動作全体がインターフェイスで表現されます。一方、テンプレートメソッドはコードの重複を減らすために使用され、定型コードは基本フレームワークまたは抽象クラスで定義されます。テンプレートメソッドでは、デフォルトの実装を備えた具象クラスが存在する場合もあります。
  4. 簡単に言うと、Strategyパターンで戦略全体(アルゴリズム)を変更できますが、テンプレートメソッドでは、一部の変更(アルゴリズムの一部)のみが行われ、残りの変更は行われません。テンプレートメソッドでは、不変ステップは抽象基本クラスに実装されますが、可変ステップにはデフォルトの実装が与えられるか、まったく実装されません。テンプレートメソッドでは、コンポーネントデザイナーはアルゴリズムの必要なステップとステップの順序を要求しますが、コンポーネントクライアントはこれらのステップのいくつかを拡張または置換できます。

画像はバイト化されたブログから取得されます。


19

継承と集約(is-aとhas-a)。同じ目標を達成するには2つの方法があります。

この質問は、選択肢間のトレードオフのいくつかを示しています:継承と集約


11

どちらも非常によく似ており、どちらもクライアントコードによって同様の方法で使用されます。上記の最も一般的な答えが言うこととは異なり、どちらも実行時にアルゴリズムを選択できます

2つの違いは、戦略パターンでは異なる実装で目的の結果を達成するための完全に異なる方法を使用できる一方で、テンプレートメソッドパターンは結果を達成するために使用される包括的なアルゴリズム(「テンプレート」メソッド)を指定することです。 -特定の実装(サブクラス)に残された唯一の選択肢は、上記のテンプレートメソッドの特定の詳細です。これは、テンプレートメソッドで1つ以上の抽象を呼び出すことによって行われます。、テンプレートメソッド自体が抽象的でなく、サブクラスによってオーバーライドされないテンプレートメソッドとは異なり、テンプレートメソッドがサブクラスによってオーバーライドされる(つまり実装される)メソッドをます。 。

クライアントコードは、ストラテジーパターンを使用するときと同じように実行時に決定できる具象サブクラスの1つのインスタンスを指す抽象クラスタイプの参照/ポインターを使用して、テンプレートメソッドを呼び出します。


9

テンプレートメソッド:

  1. それは継承に基づいています
  2. サブクラスで変更できないアルゴリズムのスケルトンを定義します。サブクラスでオーバーライドできるのは特定の操作のみです
  3. 親クラスはアルゴリズムを完全に制御し、具体的なクラスとの特定のステップのみが異なります
  4. バインドはコンパイル時に行われます

Template_method構造:

ここに画像の説明を入力してください

戦略:

  1. 委任/構成に基づいています
  2. オブジェクトの根性を変えるメソッドの動作を変更するにより変更します
  3. アルゴリズムのファミリー切り替えるために使用されます
  4. 実行時に1つのアルゴリズムを他のアルゴリズムに完全に置き換えることにより、実行時にオブジェクトの動作を変更します
  5. バインドは実行時に行われます

戦略構造:

ここに画像の説明を入力してください

理解を深めるために、テンプレートメソッド戦略の記事をご覧ください。

関連記事:

JDKのテンプレート設計パターンで、順番に実行されるメソッドのセットを定義するメソッドが見つかりませんでした

戦略パターンの実例


3

いいえ、必ずしも同じように消費されるわけではありません。「テンプレートメソッド」パターンは、将来の実装者に「ガイダンス」を提供する方法です。「すべてのPersonオブジェクトには社会保障番号が必要です」と言っています(これは簡単な例ですが、正しく理解できます)。

戦略パターンでは、複数の可能な実装を切り替えることができます。これは(通常は)継承によって実装されるのではなく、呼び出し側に目的の実装を渡させることによって実装されます。例としては、ShippingCalculatorに税金を計算するいくつかの異なる方法(NoSalesTax実装、およびPercentageBasedSalesTax実装)の1つを提供できるようにすることが考えられます。

そのため、クライアントは実際にどの戦略を使用するかをオブジェクトに伝えます。のように

myShippingCalculator.CalculateTaxes(myCaliforniaSalesTaxImpl);

しかし、クライアントは、テンプレートメソッドに基づいたオブジェクトに対してこれを行うことはありません。実際、クライアントは、オブジェクトがテンプレートメソッドに基づいていることさえ知らない場合があります。テンプレートメソッドパターンのこれらの抽象メソッドは保護されている場合もあり、その場合、クライアントはそれらが存在することさえ知りません。


3

この記事を読むことをお勧めします。実際のケースの違いを説明します。

記事からの引用

実装クラスもテンプレートメソッドクラスに依存していることがわかります。アルゴリズムの一部のステップを変更したい場合は、この依存関係によりテンプレートメソッドが変更されます。反対側の戦略では、アルゴリズムが完全にカプセル化されます。実装により、アルゴリズムを完全に定義するクラスです。したがって、変更があった場合は、以前に作成したクラスのコードを変更する必要があります。これが、クラスを設計するための戦略を選択した主な理由でした。

テンプレートメソッドの1つの特徴は、テンプレートメソッドがアルゴリズムを制御することです。これは他の状況では良いことかもしれませんが、私の問題では、これがクラスの設計を制限していました。一方、戦略はアルゴリズムのステップを制御しないため、完全に異なる変換方法を追加できます。したがって、私の場合、戦略は実装に役立ちます。

戦略の1つの欠点は、コードの冗長性が高すぎ、コード共有が少ないことです。この記事の提示された例から明らかなように、4つのクラスで同じコードを何度も繰り返す必要があります。したがって、すべてに共通のステップ4などのシステムの実装が変更された場合、5つのクラスすべてでこれを更新する必要があるため、維持するのは困難です。一方、テンプレートメソッドでは、スーパークラスのみを変更でき、変更はサブクラスに反映されます。したがって、テンプレートメソッドは、クラス間で非常に低い冗長性と大量のコード共有を提供します。

戦略では、実行時にアルゴリズムを変更することもできます。テンプレートメソッドでは、オブジェクトを再初期化する必要があります。この戦略の機能により、柔軟性が大幅に向上します。設計の観点からは、継承よりも構成を優先する必要があります。したがって、戦略パターンを使用することも、開発の主な選択肢となりました。」


2

テンプレートパターンは、戦略パターンに似ています。これら2つのパターンは、範囲と方法が異なります。

戦略は、呼び出し側がさまざまなタイプの税を計算する方法など、アルゴリズム全体を変更できるようにするために使用され、テンプレートメソッドは、アルゴリズムのステップを変更するために使用されます。このため、戦略はより粗くなります。テンプレートを使用すると、一連の操作でより細かい制御が可能になり、さらにこれらの詳細の実装を変えることができます。

その他の主な違いは、テンプレートメソッドが継承を使用するのに対して、戦略は委任を使用することです。ストラテジーでは、アルゴリズムはサブジェクトが参照する別のxxxStrategyクラスに委任されますが、テンプレートを使用すると、ベースメソッドとオーバーライドメソッドをサブクラス化して変更を加えます。

http://cyruscrypt.blogspot.com/2005/07/template-vs-strategy-patterns.htmlから


2

戦略パターンでは、サブクラスがショーを実行し、アルゴリズムを制御します。ここで、コードはサブクラス間で複製されます。アルゴリズムの知識とその実装方法は、多くのクラスに分散されています。

テンプレートパターンでは、基本クラスにアルゴリズムがあります。サブクラス間の再利用を最大化します。アルゴリズムは1か所にあるため、基本クラスがそれを保護します。


2

戦略デザインパターン

  • 構成をサポートします。
  • 実行時にオブジェクトの動作を変更する柔軟性を提供します。
  • クライアントコードとソリューション/アルゴリズムコード間の結合が少ない。

テンプレートメソッドデザインパターン

  • 構成より継承を優先
  • 基本クラスでアルゴリズムを定義します。アルゴリズムの個々の部分は、子クラスでカスタマイズできます。

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 リポジトリを確認してください。


0

ストラテジーは、抽象クラスとしてインターフェースおよびテンプレートメソッドとして公開されます。これは通常、フレームワークでよく使用されます。たとえば、SpringフレームワークのMessageSourceクラスは、メッセージを解決するための戦略インターフェースです。クライアントは、このインターフェースの特定の実装(戦略)を使用します。

また、同じインターフェースAbstractMessageSourceの抽象実装。これには、メッセージ解決の共通実装があり、サブクラスがそれらの方法でそれらを実装できるようにresolveCode()抽象メソッドを公開します。AbstractMessageSourceはテンプレートメソッドの例です。

http://docs.spring.io/spring/docs/4.1.7.RELEASE/javadoc-api/org/springframework/context/support/AbstractMessageSource.html


0

このデザインパターンのテンプレートメソッドでは、1つまたは複数のアルゴリズムステップをサブクラスでオーバーライドして、包括的なアルゴリズムを確実に実行しながら、異なる動作を許可できます(Wiki)。

パターン名テンプレートメソッドは、それが何であるかを意味します。メソッドCalculateSomething()があり、このメソッドをテンプレート化したいとします。このメソッドは、基本クラスで非仮想メソッドとして宣言されます。メソッドが次のようになっているとします。

CalculateSomething(){
    int i = 0;
    i = Step1(i);
    i++;
    if (i> 10) i = 5;
    i = Step2(i);
    return i;

Step1およびStep2メソッドの実装は、派生クラスによって提供できます。

戦略パターンでは、ベースによって提供される実装はありません(これが、ベースが実際にクラス図のインターフェースである理由です)

古典的な例はソートです。ソートする必要のあるオブジェクトの数に基づいて、適切なアルゴリズムクラス(マージ、バブル、クイックなど)が作成され、アルゴリズム全体が各クラスにカプセル化されます。

これで、並べ替えをテンプレートメソッドとして実装できますか?確かにできますが、抽象化して基本実装に配置する共通点はほとんどありません。そのため、テンプレートメソッドパターンの目的に反します。

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