デザインパターン: Template Methodパターン -具体的な処理をサブクラスに任せる-
はじめに
GoFの23のデザインパターンがまとめられている「Java言語で学ぶデザインパターン入門」を読んでアウトプットとしてデザインパターンを1つずつ記事としてアウトプットしていきます。
原則的にJavaで実装コード例などを記述していきますが、気になったことや改善点、感想等ありましたらぜひコメントくださると嬉しいです!
23のパターン一覧はこちらから ※随時更新中
Template Methodパターンとは
Template Methodパターンのイメージ
「テンプレート」とは文字の形に穴が空いている薄いプラスチックの板のことで、ペンでなぞると手書きでも整った文字が書けます。鉛筆でなぞれば鉛筆の文字、サインペンでなぞればサインペンで書いた文字、色鉛筆でなぞれば色付きの文字になルでしょう。しかしどの筆記用具を使っても書かれた文字は同じ形になるはずです。
Template Methodパターンはスーパークラスにテンプレートとなるメソッドが抽象メソッドで定義されています。そしてその抽象メソッドを実際に実装し具体的な処理を決定するのがサブクラスです。しかし、サブクラスでどのような処理をしたとしても処理の大きな流れはスーパークラスで組み立てた通りになります。
実装例
Template Methodパターンのクラス図
public abstract class AbstractDisplay {
// open, print, closeはサブクラスに実装を任せる抽象メソッド
public abstract void open();
public abstract void print();
public abstract void close();
// displayはAbstractDisplayで実装しているメソッド
public final void display(){
open();
for(int i = 0; i < 5; i++){
print();
}
close();
}
}
AbstractDisplayクラスはopen、print、close、displayというメソッドを持っているクラスで、displayメソッド以外は抽象メソッドで、displayメソッドのみが実装されています。
displayメソッドでは
- openメソッドを呼び出す
- printメソッドを5回呼び出す
- closeメソッドを呼び出す
という処理を行っています。しかしAbstractDisplayクラスではopen、print、closeの各メソッドは抽象メソッドになっているためdisplayメソッドが「実際に」何をするのかは各メソッドを実装するサブクラスに任されています。
ではそのサブクラスについて解説していきます。
CharDisplayクラスの各メソッドの処理
メソッド名 | 処理 |
---|---|
open | 文字列"<<"を表示する |
コンストラクタで与えられた1文字を表示する | |
close | 文字列">>"を表示する |
public class CharDisplay extends AbstractDisplay {
// 表示する文字
private char ch;
// コンストラクタ
public CharDisplay(char ch) {
this.ch = ch;
}
@Override
public void open(){
// 開始文字列として"<<"を表示する
System.out.println("<<");
}
@Override
public void print(){
// フィールドに保存しておいた文字を一回表示する
System.out.println(ch);
}
@Override
public void close(){
// 終了文字列として">>"を表示する
System.out.println(">>");
}
}
StringDisplayクラスの各メソッドの処理
メソッド名 | 処理 |
---|---|
open | 文字列"+-----+"を表示する |
コンストラクタで与えられた文字列を"|"と"|"で挟んで表示する | |
close | 文字列"+-----+"を表示する |
public class StringDisplay extends AbstractDisplay {
// 表示する文字列
private String string;
// 文字列の表示幅
private int width
// コンストラクタ
public StringDisplay(Strign string) {
this.string = string;
this.width = string.length();
}
@Override
public void open(){
printLine();
}
@Override
public void print(){
System.out.println("|" + string + "|");
}
@Override
public void close(){
printLine();
}
// openとcloseから呼び出されて"+----+"という文字列を表示するメソッド
private void printLine() {
System.out.print("+");
for(int i = 0; i < width; i++) {
System.out.print("-");
}
System.out.print("+");
}
}
最後に動作を確認するためのMainクラスです。先ほど作ったCharDisplayクラスとStringDisplayクラスのインスタンスを作り、displayメソッドを呼び出しています。
public class Main() {
public static void main(String[] args) {
// 'A'を持ったCharDisplayのインスタンスを1個作る
AbstractDisplay charDisplay = new CharDisplay('A');
// "Hello, World."を持ったStringDisplayのインスタンスを1個作る
AbstractDisplay stringDisplay = new StringDisplay("Hello, World.");
// 作成したインスタンスは2つとも同じスーパークラスのサブクラスのインスタンスなので
// 継承したdisplayメソッドを呼び出せる
charDisplay.display();
stringDisplay.display();
}
}
実行結果
<<AAAAA>> ←charDisplayによる表示
+------------+ ←stringDisplayによる表示
|Hello, World|
|Hello, World|
|Hello, World|
|Hello, World|
|Hello, World|
+------------+
Template Methodパターンの有用な例
既存のソースをリファクタリングする時
- 「前処理、メイン処理、後処理」といったように複数の具象クラスの処理の流れが似ている時
アルゴリズムを一連のステップに分解し、これらのステップをメソッドに書き換え、 単一のテンプレート・メソッドからこれらのメソッドを順番に呼び出すようにします。
ステップは、abstract
としてもいいですし、何らかのデフォルトの実装を用意してもかまいません。アルゴリズムを利用するためには、クライアントは独自のサブクラスを用意し、全抽象メソッドを実装し、必要ならば他のメソッド(ただしテンプレート・メソッドは除く)も上書きする決まりになっています。
まとめ
Template Methodパターンとは
Template Methodパターンは、抽象クラスに共通の処理を実装し、派生クラスに個別の処理を実装させることで、アルゴリズムの構造を変えずに処理の流れをカスタマイズできるデザインパターンです。
このパターンを適用することで、処理の流れを固定することができ、柔軟性が高くなります。また、共通の処理を抽象クラスに実装することで、処理の共通化が実現できます。
しかし、Template Methodパターンは、処理の流れが決まっている場合に限定されます。また、処理の流れが複雑になる場合には、パターンを適用することが困難になる場合もあります。
Template Methodパターンは、コードの再利用性を高め、メンテナンス性を向上させることができるため、大規模なプロジェクトで使用することが多いです。
Discussion