1. 概要

このクイックチュートリアルでは、最も人気のあるGoFパターンの1つであるテンプレートメソッドパターンを活用する方法を説明します。

ロジックを単一のメソッドにカプセル化することにより、複雑なアルゴリズムの実装が容易になります。

2. 実装

テンプレートメソッドパターンがどのように機能するかを示すために、コンピュータステーションの構築を表す簡単な例を作成しましょう。

パターンの定義が与えられると、アルゴリズムの構造は、テンプレートbuild()メソッドを定義する基本クラスで定義されます。

public abstract class ComputerBuilder {
    
    // ...
    
    public final Computer buildComputer() {
        addMotherboard();
        setupMotherboard();
        addProcessor();
        return new Computer(computerParts);
    }
   
    public abstract void addMotherboard();
    public abstract void setupMotherboard();
    public abstract void addProcessor();
    
    // ...
}

ComputerBuilderクラスは、マザーボードやプロセッサなどのさまざまなコンポーネントを追加および設定するためのメソッドを宣言することにより、コンピューターを構築するために必要な手順の概要を説明します。

ここで、 build()メソッドはテンプレートメソッドであり、コンピューターパーツを組み立てるためのアルゴリズムのステップを定義し、完全に初期化されたComputerインスタンスを返します。

オーバーライドされないように、itがfinalとして宣言されていることに注意してください。

3. 動作中

基本クラスがすでに設定されているので、2つのサブクラスを作成してそれを使用してみましょう。 1つは「標準」コンピューターを構築し、もう1つは「ハイエンド」コンピューターを構築します。

public class StandardComputerBuilder extends ComputerBuilder {

    @Override
    public void addMotherboard() {
        computerParts.put("Motherboard", "Standard Motherboard");
    }
    
    @Override
    public void setupMotherboard() {
        motherboardSetupStatus.add(
          "Screwing the standard motherboard to the case.");
        motherboardSetupStatus.add(
          "Pluging in the power supply connectors.");
        motherboardSetupStatus.forEach(
          step -> System.out.println(step));
    }
    
    @Override
    public void addProcessor() {
        computerParts.put("Processor", "Standard Processor");
    }
}

そして、これがHighEndComputerBuilderバリアントです。

public class HighEndComputerBuilder extends ComputerBuilder {

    @Override
    public void addMotherboard() {
        computerParts.put("Motherboard", "High-end Motherboard");
    }
    
    @Override
    public void setupMotherboard() {
        motherboardSetupStatus.add(
          "Screwing the high-end motherboard to the case.");
        motherboardSetupStatus.add(
          "Pluging in the power supply connectors.");
        motherboardSetupStatus.forEach(
          step -> System.out.println(step));
    }
    
    @Override
    public void addProcessor() {
         computerParts.put("Processor", "High-end Processor");
    }
}

ご覧のとおり、アセンブリプロセス全体について心配する必要はありませんでしたが、個別のメソッドの実装を提供するためだけでした。

それでは、実際の動作を見てみましょう。

new StandardComputerBuilder()
  .buildComputer();
  .getComputerParts()
  .forEach((k, v) -> System.out.println("Part : " + k + " Value : " + v));
        
new HighEndComputerBuilder()
  .buildComputer();
  .getComputerParts()
  .forEach((k, v) -> System.out.println("Part : " + k + " Value : " + v));

4. Javaコアライブラリのテンプレートメソッド

このパターンは、Javaコアライブラリで広く使用されています。たとえば、java.util.AbstractListjava.util.AbstractSet。などです。

たとえば、 Abstract List は、Listインターフェースの骨格実装を提供します。

テンプレートメソッドの例としては、 addAll()メソッドがありますが、 final:として明示的に定義されていません。

public boolean addAll(int index, Collection<? extends E> c) {
    rangeCheckForAdd(index);
    boolean modified = false;
    for (E e : c) {
        add(index++, e);
        modified = true;
    }
    return modified;
}

ユーザーは、 add()メソッドを実装するだけで済みます。

public void add(int index, E element) {
    throw new UnsupportedOperationException();
}

ここで、指定されたインデックス(リストアルゴリズムのバリアント部分)でリストに要素を追加するための実装を提供するのはプログラマーの責任です。

5. 結論

この記事では、テンプレートメソッドパターンとそれをJavaで実装する方法を示しました。

テンプレートメソッドパターンは、コードの再利用とデカップリングを促進しますが、継承を使用することを犠牲にします。

いつものように、この記事に示されているすべてのコードサンプルは、GitHubから入手できます。