導入
コーヒーも紅茶も、作り方の大まかな流れ(お湯を沸かす → 抽出する → カップに注ぐ)は同じで、違うのは「抽出する」の中身だけです。この共通の流れを毎回書き直すのは無駄です。Template Method(テンプレートメソッド) は、処理の骨格を親クラスで固定し、変わる部分だけを子クラスに任せる パターンです。
説明
親クラス(抽象クラス)が「決まった順番で呼び出す」骨格メソッドを持ち、その中の一部だけを abstract にして子クラスに実装させます。
public class Main {
public static void main(String[] args) {
Drink coffee = new Coffee();
coffee.prepare();
Drink tea = new Tea();
tea.prepare();
}
}
abstract class Drink {
final void prepare() {
boilWater();
brew();
pourInCup();
}
void boilWater() {
System.out.println("お湯を沸かす");
}
abstract void brew();
void pourInCup() {
System.out.println("カップに注ぐ");
}
}
class Coffee extends Drink {
void brew() {
System.out.println("コーヒーを抽出する");
}
}
class Tea extends Drink {
void brew() {
System.out.println("紅茶を蒸らす");
}
}
classDiagram
Drink <|-- Coffee
Drink <|-- Tea
class Drink {
<<abstract>>
+prepare() final
+boilWater()
+brew()* abstract
+pourInCup()
}
final void prepare() { boilWater(); brew(); pourInCup(); }… これが「手順の骨格」です。finalを付けて、子クラスがこの順番自体は上書きできないようにしています。abstract void brew();… 「抽出する」の中身だけは子クラスごとに違うので、抽象メソッドにして子クラスに実装を強制します。Coffee・Teaはそれぞれbrew()だけをオーバーライドします。boilWater()とpourInCup()は共通実装のまま何も書かずに使われます。coffee.prepare()を呼ぶと、Drinkに書かれた順番通りに実行され、brew()の部分だけCoffeeの実装(コーヒーを抽出する)に動的に差し替わって呼ばれます(第1章オブジェクト指向コースで学んだ動的束縛の応用です)。
やってみよう
下のエディタを実行し、Coffee と Tea でそれぞれ「お湯を沸かす」「カップに注ぐ」が共通で、間の一行だけが違うことを確認しましょう。
演習
Juice という Drink のサブクラスを作り、brew() をオーバーライドして ジュースを絞る と表示させてください。main で Drink juice = new Juice(); juice.prepare(); を呼んでください。
ヒント1を見る
class Juice extends Drink { void brew() { System.out.println("ジュースを絞る"); } }。
ヒント2を見る
boilWater() と pourInCup() はオーバーライドしなくても、Drink の実装がそのまま使われます。