デザインの禅——テンプレート方法モード

13340 ワード

一、引用
テンプレートメソッドモデルは,我々の普段の開発において非常によく見られるものであり,非常に理解しやすく,普段何気なく使用されるため,その設計思想を理解する必要がある.
二、定義
「Head First設計モード」という本では、テンプレートメソッドモードを以下のように定義しています.
テンプレートメソッドモードは、1つのメソッドでアルゴリズムのスケルトンを定義し、いくつかのステップをサブクラスに遅延します.テンプレートメソッドにより、サブクラスは、アルゴリズム構造を変更することなく、アルゴリズムのいくつかのステップを再定義することができる.
一般的に言えば、固定的なアルゴリズムステップを定義する必要がありますが、各ステップは、お客様が継承することによってカスタマイズを実現することができます.これにより、拡張の開放、閉鎖の原則の修正、コードの多重化、保証コードの拡張性が大幅に実現されます.同時にテンプレートメソッドモデルは、ハリウッドの原則に従う必要があります.ハリウッドの原則とは何ですか?
私たちを呼び出さないでください.私たちはあなたを呼び出します.
これは、高レベルコンポーネント(親)が低レベルコンポーネントに対処する方法であり、簡単に言えば、高レベルコンポーネントは低レベルコンポーネントを呼び出すことができ、低レベルコンポーネントは高レベルコンポーネントを直接呼び出すことができません.どうしてこんなことになったの?想像してみてください.高層コンポーネントは低層コンポーネントに依存し、低層コンポーネントは高層コンポーネントに依存し、高層コンポーネントはサイドコンポーネントに依存し、サイドコンポーネントは低層コンポーネントに依存しています.「依存腐敗」、すなわち環状依存を形成します.この場合、システムの設計を簡単に理解するのは非常に難しいので、良い設計はハリウッドの原則に従うべきです.
三、コード実装
テンプレートの作り方パターンは生活の実例にも対応していますが、ここでは料理や炒め物で説明します.炒め物はすべて野菜を洗うことを経験しなければならなくて、野菜を切って、炒め物、鍋を出すなどのいくつかの過程を経験して、そのためこれは1つの固定的な過程で、私はそれを抽象的に炒め物の前に準備して、炒め物の中でおよび皿を並べる3つの過程をアルゴリズムの骨格として、doCooking方法の中に置いて、そしてこの方法をfinalに設定して、その行為が変えられないことを保証します;preCooking()とputPlate()の仮定は同じですが、具体的にどの料理を作るかはサブクラスで実現され、コードは以下の通りです.
public abstract class Vegetables {
	
    public final void doCooking() {
        preCooking();
        cooking();
        putPlate();
    }

    private void preCooking() {
        System.out.println("Wash vegetables and light a fire!");
    }

    protected abstract void cooking();

    private void putPlate() {
        System.out.println("Put plate!");
    }

}

public class Potato extends Vegetables {

    @Override
    protected void cooking() {
        System.out.println("    !");
    }

}

コードは非常に簡単で、お客様が何を注文するかは、対応するサブクラスをインスタンス化し、親のdoCookingメソッドを呼び出すことで対応する料理を作ることができます.もしテンプレートの方法がなければ、すべての料理はすべての流れを実現しなければならなくて、大量の繰り返しの仕事は1度の災難で、コードも非常に肥大しているように見えて、テンプレートの方法を通じて私达は変化の部分を解結合して、大いにコードの量を減らして、コードの多重化を実現します.しかし、私のところには抽象的なプロセスが1つしかありません.もし親クラスに複数の抽象的なプロセスが定義されていたら?その下のすべてのサブクラスもすべての方法を一度実現する必要があるので,抽象的なステップの区分には必ず度を把握しなければならない.上記の実装はかなり良いように見えますが、最も基本的な実装にすぎず、実際の複雑なビジネスを満たすことはできません.あるステップが必要でない場合、どのように処理すればいいですか?例えば、お客様が「キュウリの和え物」を注文したとき、皮をむくように要求するお客様もいれば、使わないお客様もいます.上の例でどうするか考えてみましょう.条件制御として親にフックメソッドを追加するだけでいいです.次のようにします.
public abstract class Vegetables {

    public final void doCooking() {
        preCooking();
        if (isPeel()) {
            peel();
        }
        cooking();
        putPlate();
    }

    private void preCooking() {
        System.out.println("Wash vegetables and light a fire!");
    }

    private void peel() {
        System.out.println("Peel!");
    }

    protected abstract void cooking();

    private void putPlate() {
        System.out.println("Put plate!");
    }

	//     
    protected boolean isPeel() {
        return true;
    }

}


親には、フックメソッドisPeel()によって実行する必要があるかどうかを制御するpeelステップが追加されます.親は一般的にデフォルトで制御する必要がありますが、実際の条件ロジックは、子によって上書きされます.
public class Cucumber extends Vegetables {

    @Override
    protected void cooking() {
        System.out.println("    !");
    }

    @Override
    protected boolean isPeel() {
        Scanner sc = new Scanner(System.in);
        String str = sc.next();

        if ("n".equals(str)) {
            return false;
        }
        return true;
    }
}

毎回、お客様に「皮むき」が必要かどうかを尋ねます.お客様が「n」と入力した場合、peel()メソッドは実行されません.そうしないと実行されます.他のメニューはこのメソッドを上書きしても上書きしなくてもいいし、デフォルトを上書きしなくても皮むきになります.また,ステップの実行の有無をサブクラスに渡して制御することで,より柔軟になる.
四、まとめ
テンプレートメソッドモードの実装は簡単で、理解も難しくなく、実際の応用も非常に広範であるが、完璧ではない.上述した抽象的なステップが多すぎると、実装もかなり複雑であり、実装を増やすたびにクラスを追加する必要があり、システムがますます膨大になる.そのため、実際の設計過程でもよく考えなければならない.同時に、テンプレートの方法は実際にはjava apiのArraysのような多くの変種があることが多い.sort()メソッドおよび前回述べた工場メソッドは実際にはテンプレートメソッドモードで実現されており,各モードの設計思想を真に理解し,優れたコード設計を多く見てこそ,万般の変化に対応することができる.最後に「Head First設計モード」のテンプレートメソッドモードのまとめのポイントを貼って、共に進歩します!
  • テンプレート法はアルゴリズムのステップを定義し、これらのステップの実現をサブクラスに遅延させる.
  • サブクラスがテンプレートメソッドのアルゴリズムを変更することを防止するために、テンプレートメソッドをfinalとして定義することができる.
  • フックは、抽象クラスで仕事をしないか、デフォルトのことだけをして、サブクラスが上書きするかどうかを選択する方法です.
  • ハリウッドの原則は、意思決定権を上層コンポーネントに置いて、低層コンポーネントをどのように呼び出すか、いつ呼び出すかを決定することを教えてくれます.
  • ポリシーモードとテンプレート方法はアルゴリズムをカプセル化し、1つは組み合わせで、1つは継承である.
  • 工場法は特殊なテンプレート法である.