はじめに
こちらの記事ではTemplate Methodパターンについて説明します。
抽象クラスとインタフェースの違いについては必ず理解しておく必要があります。
このステップを飛ばしてデザインパターンに入っても、使いこなすことは出来ないと思います。
こちらの記事がとても参考になると思うので、一度目を通して見てください。
テンプレートって?
日本語でいうと型です。
メールを作成する時もある程度テンプレを作っておく事で、仕事がはかどったりするかと思います。
プログラムでも同じ事が言えます。
Template Methodパターンの概念
処理の共通部分を抽象クラスに抽出し、固有の処理を具象クラスで実装する手法。
もっと簡単にいうと似たような流れの処理を共通化したい時に使えます。
この流れが重要だと思っています。ただ共通化出来る処理を抽象クラスに持っていくのではなく、
テンプレート(型)として、処理の流れを抽象クラスに抽出する事がこのパターンの醍醐味です。
どんな時に使えるか?
以下のような時にこのパターンを使う事が出来ると思います。
「前処理、メイン処理、後処理」といったように複数の具象クラスの処理の流れが似ている時
このいつ使えるかを見極めるのが難しく、一番重要なところなので一度ご自身が書いたプログラムや、
公開されているソースコードをみて考えてみるといいと思います。
Template Methodパターンの使用イメージ
学生の1日と、社会人の1日を例にしてテンプレートメソッドパターンを使ってみましょう。
学生 | 社会人 | |
---|---|---|
起床 | 起床 | ←同じ行動 |
二度寝 | 準備 | ←違う行動 |
遊ぶ | 仕事 | ←違う行動 |
就寝 | 就寝 | ←同じ行動 |
学生と社会人の一日の行動パターン(型)を書いてみました。
このようにパターン(型)が似ている時にテンプレートメソッドパターンが使えます!
人の生活の流れを抽象クラスに書き(共通の行動は実装する)、違う行動をそれぞれの具象クラスで実装すれば、簡単にですが、テンプレートメソッドパターンの完成です。
サンプルプログラム
上記で示したものを例にとって実際に実装してみます。
人の生活の流れを抽象クラスで実装します。
起床と就寝は同じ行動なので抽象クラスで実装しちゃいます。
public abstract class Human {
public void startDay(){
getUp();
preAction();
mainAction();
sleep();
}
protected abstract void mainAction();
protected abstract void preAction();
private void getUp(){
System.out.println("起床");
}
private void sleep(){
System.out.println("就寝");
}
}
学生特有の二度寝と、遊びをこの具象クラスで実装します。
public class Student extends Human{
@Override
protected void mainAction() {
System.out.println("二度寝");
}
@Override
protected void preAction() {
System.out.println("遊ぶ");
}
}
社会人特有の、準備と仕事をこの具象クラスで実装します。
public class WorkingAdult extends Human{
@Override
protected void mainAction() {
System.out.println("準備");
}
@Override
protected void preAction() {
System.out.println("仕事");
}
}
最後にこれら上記のクラスを呼び出すクラスを作成します。
学生と社会人をnewして、1日を開始するだけ
public class Main {
public static void main(String[] arg){
Human student = new Student();
Human worker = new WorkingAdult();
System.out.println("**学生**");
student.startDay();
System.out.println("**社会人**");
worker.startDay();
}
}
/*
**学生**
起床
遊ぶ
二度寝
就寝
**社会人**
起床
仕事
準備
就寝
*/
どうでしたか?
案外簡単でしたね!むしろただ共通処理を抽象クラスに持っていっただけ!?
ここでしっかり理解してほしいところは処理の流れを抽象クラスに持っていったところです。
Composed Method パターンとの組み合わせ
ここからはちょっと応用編。
Composed Method パターンと言うものがありますが、ただメソッド抽出を行い、
処理を読んでわかるくらいまで可読性あげようぜ!!って手法。
このComposed Method パターンとTemplate Methodパターンを組み合わせると
どんな良いことがあるのか?
例えば、似たような処理だけど、若干異なっていて共通化出来ない。。。
だからTemplate Methodパターン使えないなーって時にこの手法を組み合わせると出来るようになる。
※なんとなく使えてる人も多いと思う。実際に自分もそうだった。
ではコーヒーと紅茶を作成する処理を例にとってみる。
コーヒーを作成するクラス
public class Coffee {
public void makeCoffee(){
createHotWater();
System.out.println("コーヒーの粉を入れる");
waiting();
}
private void createHotWater() {
System.out.println("沸騰水を作る");
}
private void waiting(){
System.out.println("待つ");
}
}
紅茶を作成するクラス
public class Tea {
public void makeCoffee(){
createHotWater();
System.out.println("紅茶の粉を入れる");
waiting();
}
private void createHotWater() {
System.out.println("沸騰水を作る");
}
private void waiting(){
System.out.println("待つ");
}
}
お!!!「沸騰水を作って、コーヒーor紅茶を入れて、待つ」似た流れの処理だ!!
Template Methodパターンを使おう!!!!
試しに実装してみてほしい。気づくことがあると思う。
メソッド化されていない、「コーピの粉を入れる」、「紅茶の粉を入れる」の部分がこのままでは
共通化出来ない。
ここでComposed Method パターンの登場だ!
コーヒークラスを以下のように書き換えてみる。
コードの断片をputPowderメソッドに抽出しただけ
public class Coffee {
public void makeCoffee(){
createHotWater();
putPowder();
waiting();
}
protected void putPowder() {
System.out.println("コーヒーの粉を入れる");
}
private void createHotWater() {
System.out.println("沸騰水を作る");
}
private void waiting(){
System.out.println("待つ");
}
}
紅茶クラスを以下のように書き換えてみる。
コードの断片をputPowderメソッドに抽出しただけ
public class Tea {
public void makeCoffee(){
createHotWater();
putPowder();
waiting();
}
protected void putPowder() {
System.out.println("コーヒーの粉を入れる");
}
private void createHotWater() {
System.out.println("沸騰水を作る");
}
private void waiting(){
System.out.println("待つ");
}
}
ここまでくれば後はメソッドを抽象メソッドに引き上げて、具体的な処理は具象クラスで実装すれば良いだけです。
まとめ
Template Methodパターンの用途がお分りいただけたでしょうか?
新規で作成する際の使い方はとてもシンプルですが、既存のコードをリファクタリングする場合は、
「どこらへんが共通化出来そう」などと見極めることが案外難しかったりします。
また、抽象クラスでは出来るだけ抽象的なメソッド名にした方が良いです。
どんどん具象クラスの数が増えていった場合に固有の名前を書いていると、
処理内容とメソッド名が合わなくなってきます。