デザインパターン入門① Template Method
これまでデザインパターンの存在は知っていたが、きちんと勉強したことが無かったので、こちらの書籍とこちらの記事を参考に学習を進めている。
今回は第一弾として、Template Methodについて書いていこうと思う。
Templete Methodとは
一言で言ってしまえば、具体的な処理をサブクラスに任せるということを示したパターンのこと。
スーパークラスを定義し、その中では大まかな処理の枠組みを定義する。
そして、それらをサブクラスで継承し、具体的な処理はサブクラスで実装する、というものです。
クラス図で表すとこんな感じ。
斜体で書かれているメソッドはabstractメソッドであることを示しており、 ConcreteClass1,2で具体的な処理を実装しています。
また、上のクラス図ではAbstractClassに一つだけ具象メソッドがありますが、 この具象メソッドの中で具体的な処理の流れを定義しております。
具体例に置き換えて考える。
それぞれ下記の内容に置き換えて考えてみます。
AbstractClass : 動物
ConcreteClass1 : 人間
ConcreteClass2 : ネコ
人間、ネコはどちらも共通して「動物」であり、起きる()、食べる()、寝る()という共通の行動を取ります。 しかし、人間とネコでは食べ方や寝る姿勢、起きる姿勢などに違いがあります。
これをクラス図にして考えると、このような形になります。
つまり、このパターンで行っていることは、
「動物が取る行動は起きる、食べる、寝るで共通しているから、わざわざ「動物」に該当する各クラスで毎回実装させずに「動物」クラスで定義するけど(wakeUpAndEatAndSleep())、 人間とかネコでそれらの行動の特徴に違いはあるだろうから、そういうのは各々自分のクラスで実装してね。(wakeUp(),eat(),sleep())」
といったことをしていると考えることができる。
メリデメ
メリット
- ロジックの共通化、サブクラス設計の簡潔化
TemplateMethodパターンを用いずに、コピペで複数の「動物」の実装クラス「人間」、「ネコ」、「イヌ」、、、を作成した場合、 修正の範囲が全体に及んでしまいます。
動物として取りうる行動が共通しているのであれば、各サブクラスで実装するのではなくスーパークラス側で共通の処理として持たせることができ、 そうすることでサブクラスの設計が簡潔になり、修正が必要になった際もスーパークラスのロジック一箇所の修正で対応が可能になります。
デメリット
- 親と子の関係が密接なので、スーパークラスで定義している処理の内容を把握する必要性アリ
サブクラス側で共通ロジックの記述は必要なくなりますが、共通ロジックの内容を意識しなくていいという訳ではありません。 「人間」「ネコ」「イヌ」のサブクラスはどれも「動物」に集約されるため、開発者は動物として取りうる行動が何なのかを把握し、 その行動に対応した細かな違いをサブクラスごとに実装していく必要があります。
- スーパークラスの処理が大きくなると、サブクラスの自由度が減少する
スーパークラス側で処理の大枠を固めてしまうため、その内容が大きければ大きいほど、 サブクラス側での自由度は減少してしまいます。
ソースコード
public abstract class Animal { public abstract void wakeUp(); public abstract void eat(); public abstract void sleep(); public void wakeUpAndEatAndSleep() { wakeUp(); eat(); sleep(); } }
public class Human extends Animal { @Override public void wakeUp() { System.out.println("おはようございます。"); } @Override public void eat() { System.out.println("いただきます"); } @Override public void sleep() { System.out.println("おやすみなさい"); } }
public class Cat extends Animal { @Override public void wakeUp() { System.out.println("にゃー"); } @Override public void eat() { System.out.println("むしゃむしゃむしゃ"); } @Override public void sleep() { System.out.println("zzz..."); } }
public class Main { public static void main(String[] args) { Animal human = new Human(); Animal cat = new Cat(); System.out.println("---人間---"); human.wakeUpAndEatAndSleep(); System.out.println("---ネコ---"); cat.wakeUpAndEatAndSleep(); } }
▼実行結果
---人間--- おはようございます。 いただきます おやすみなさい ---ネコ--- にゃー むしゃむしゃむしゃ zzz...