メインコンテンツへ移動

デザインパターン之テンプレートメソッドパターン(Template Method Pattern)

無料2015-03-07#Design_Pattern#模版方法模式#Template Method Pattern

テンプレートメソッドパターンは、アルゴリズムの骨格(フロー)をカプセル化するために使用され、特定のステップはサブクラスによって実装されます。さらに別のパターンとしてストラテジーパターンがあり、どちらもアルゴリズムをカプセル化するために使用されますが、重点が異なります。本稿で詳しく議論します。

no_mkd

一。テンプレートメソッドパターンとは何か?

まず、テンプレートメソッドパターンはアルゴリズムの骨格をカプセル化するためのものです。つまりアルゴリズムフローです

テンプレートと呼ばれるからには、もちろん拡張クラスがこのテンプレートを使用できることを許可しており、変化に対応するために、拡張クラスがいくつかの変更を行うことも許可しているはずです

事実その通りで、テンプレートメソッドパターンはアルゴリズムフローをカプセル化しますが、特定のステップの詳細を実装するのはサブクラスに任せます

二。例を挙げる

フランチャイズを許可する炸醤麺店を開くことになると仮定します。私たちには独占秘製のソースレシピと、独特な製造工藝があり、美味しさはここだけです。地球上の大多数の人が毎日私たちの炸醤麺を食べるので、大量のフランチャイジーがいます

利益を上げるために、もちろん製造工藝とソースレシピを公開することはできませんが、フランチャイジーの経営地域の差異により、製造詳細に差異があります。例えば北京人は細い硬い麺を好み、陝西人は広い柔らかい麺を好みます。レシピを保護するために北京のフランチャイジーに広い麺を売り続けさせることもできません

したがって、製造工藝と秘伝レシピを保護し、誰も変更できないようにすると同時に、麺の製造と調理方法を公開し、フランチャイジーが変更できるようにする必要があります。では、どのようにしてこのようなニーズを実現すればよいでしょうか?

そうです、テンプレートメソッドはまさにこのために存在します

まず、テンプレートクラスを定義し、保護すべきものを保護し、公開すべきものを公開します:

package TemplateMethodPattern;

/**

  • @author ayqy

  • 炸醤麺テンプレートクラスの定義 */ public abstract class Noodles { public void cook(){ //麺を製造 makeNoodles(); //ソースを製造 makeSauce(); //麺を調理 boilNoodles(); //ソースを追加 addSauce(); }

    /**

    • 秘製ソースを保護 */ private void makeSauce(){ System.out.println("独占秘製ソースを完成"); }

    /**

    • 添加量を保護 */ private void addSauce(){ System.out.println("適量の秘製ソースを追加"); }

    public abstract void makeNoodles(); public abstract void boilNoodles(); }

テンプレートを定義すれば、90% の作業は完了です。次に具体クラスを実装します

北京炸醤麺:

package TemplateMethodPattern;

/**

  • @author ayqy

  • 北京炸醤麺具体クラスの実装 */ public class BJNoodles extends Noodles{

    @Override public void makeNoodles() { System.out.println("北京特色の手打ち麺を完成"); }

    @Override public void boilNoodles() { System.out.println("北京特色の麺調理法で麺を調理"); } }

陝西炸醤麺:

package TemplateMethodPattern;

/**

  • @author ayqy

  • 陝西炸醤麺具体クラスの実装 */ public class SXNoodles extends Noodles{

    @Override public void makeNoodles() { System.out.println("陝西特色の手打ち麺を完成"); }

    @Override public void boilNoodles() { System.out.println("陝西特色の麺調理法で麺を調理"); } }

これらの具体クラスがあれば、地域ごとにフランチャイジーに配分すればよいのです

三。効果例

まずテストクラスを実装します:

package TemplateMethodPattern;

/**

  • @author ayqy

  • テストクラスの実装 */ public class Test { public static void main(String[] args){ //北京炸醤麺オブジェクトを作成 Noodles bjnoodles = new BJNoodles(); //陝西炸醤麺オブジェクトを作成 Noodles sxnoodles = new SXNoodles();

     System.out.println("北京炸醤麺製造工藝:");
     bjnoodles.cook();//北京炸醤麺を 1 份作る
     System.out.println("\n陝西炸醤麺製造工藝:");
     sxnoodles.cook();//陝西炸醤麺を 1 份作る
    

    } }

実行結果:

四。もう少し考える

上記の例では、アルゴリズムの骨格のカプセル化を簡単に実現し、拡張クラスが特定のステップの詳細をカスタマイズできるようにしました。非常に簡単で完璧に見えるかもしれません

では、ニーズを変更してみましょう:

最近、グリーンで健康的な生活が提唱され、みんな野菜を食べるのが好きです。私たちの伝統的な炸醤麺も時代に合わせて進化し、完成後に野菜を追加する必要があります。しかし問題なのは、一部の地域では野菜の追加を好まないことです。彼らは老舗炸醤麺の風味に慣れ、野菜を坚决に拒否します。したがって、製造工藝を簡単に変更して野菜を追加する工程を追加することはできません。テンプレートで選択可能な操作を定義できれば最高です。フランチャイジーが野菜を追加するかどうかを自分で選択できるように

そこで、以前のテンプレートを少し変更する必要があります:

package TemplateMethodPattern;

/**

  • @author ayqy

  • 炸醤麺テンプレートクラスの定義 */ public abstract class Noodles { private boolean wantVegetables = false;//野菜が必要かどうか

    public void setWantVegetables(boolean wantVegetables) { this.wantVegetables = wantVegetables; }

    public void cook(){ //麺を製造 makeNoodles(); //ソースを製造 makeSauce(); //麺を調理 boilNoodles();

     //野菜を追加するかどうか
     if(wantVegetables)
     	addVegetables();
     
     //ソースを追加
     addSauce();
    

    }

    /**

    • 秘製ソースを保護 */ private void makeSauce(){ System.out.println("独占秘製ソースを完成"); }

    /**

    • 添加量を保護 */ private void addSauce(){ System.out.println("適量の秘製ソースを追加"); }

    /**

    • 旬の野菜を追加 */ public void addVegetables(){ //空の実装 }

    public abstract void makeNoodles(); public abstract void boilNoodles(); }

注意、選択可能な操作を追加するために、以下のことを行いました:

  1. フラグ変数を定義(選択/非選択)
  2. フラグ変数の setter を提供し、拡張クラスが選択できるように
  3. アルゴリズムの骨格を変更し、新しい選択可能なステップを追加
  4. 新しいステップに空の実装を提供(注意、このステップは非常に重要です。空の実装を提供し、抽象メソッドを定義しないことで、既存の拡張クラスの変更を避けます。これはテンプレートメソッドパターンの用語ではHook フックと呼ばれます)

五。テンプレートメソッドパターンとストラテジーパターン

これらの 2 つのパターンはどちらもアルゴリズムをカプセル化するために使用されます。比較してみましょう:

 ストラテジーパターンテンプレートメソッドパターン
概念アルゴリズムステップをカプセル化し、サブクラスが既存のストラテジー(ステップ詳細)を選択できるようにアルゴリズムの骨格(フロー)をカプセル化し、サブクラスが特定の詳細を実装できるように
実装方式コンポジションを使用して実装継承を使用して実装
目標アルゴリズムステップの選択を実現アルゴリズムのフロー制御を実現
亮点実行時にステップ(ストラテジー)を動的に変更可能実行時にアルゴリズムフローを動的に変更可能(hook で実装)
具体ステップ変化しやすい同類のアルゴリズム詳細(ステップ)を見つけ、アルゴリズムファミリー(インターフェース)を定義してカプセル化アルゴリズムの骨格を抽象化し、基底クラス(テンプレートクラス)にカプセル化
まとめステップをカプセル化フローをカプセル化

例を挙げます:

現在、フローが A->B->C->D のアルゴリズムがあると仮定します。C ステップの具体実装には c1,c2,c3 の 3 つの異なる方法があります

ストラテジーパターン:

  1. 行動インターフェース C を定義し、execute メソッドを定義
  2. 具体クラス c1,c2,c3 をインターフェース C から拡張(c1,c2,c3 の 3 つの行動を呼び出し者が選択可能)
  3. 基底クラスにプロパティを追加し、タイプをインターフェース C とし、setter を提供
  4. 基底クラスから拡張した具体クラスは、setter を呼び出して動的に行動を変更

テンプレートメソッドパターン:

  1. 基底クラス(テンプレートクラス)を定義し、基底クラスでアルゴリズムフローを定義(フローをカプセル化)
  2. 基底クラスで C ステップを抽象メソッドとして定義
  3. 基底クラスから拡張した具体クラスは、独自の実装を提供(c1,c2,c3 または其它)

コメント

コメントはまだありません

コメントを書く