Javaによるデザインパターンの2回目です!
教材は、参考文献の「」を使わせて頂きます。
今回は、Template Methodパターンです。1回目のシングルトンパターンに比べて、少し難しくなったと思います。
では、やっていきましょう!
参考文献
参考文献のサンプルプログラムのダウンロード
はじめに
「Javaでデザインパターンを学ぶ」の記事一覧です。良かったら参考にしてください。
・第1回:Javaでデザインパターンを学ぶ:Singletonパターン
・第2回:Javaでデザインパターンを学ぶ:Template Methodパターン ← 今回
・第3回:Javaでデザインパターンを学ぶ:Observerパターン
・第4回:Javaでデザインパターンを学ぶ:Iteratorパターン
・第5回:Javaでデザインパターンを学ぶ:Factory Methodパターン
・第6回:Javaでデザインパターンを学ぶ:Stateパターン
・第7回:Javaでデザインパターンを学ぶ:Visitorパターン
・第8回:Javaでデザインパターンを学ぶ:Adapterパターン
・番外編:Javaのコンパイル方法(仕組み)をパッケージ含めていろいろ試してみる
先に、23種類のデザインパターンを示します。
参考サイト:デザインパターン (ソフトウェア) - Wikipedia
・Abstract Factory:関連するインスタンスを状況に応じて、適切に生成する方法を提供する
・Builder:複合化されたインスタンスの生成過程を隠蔽する。
・Factory Method:生成されるインスタンスに依存しない、インスタンスの生成方法を提供する
・Prototype:同様のインスタンスを生成するために、原型のインスタンスを複製する
・Singleton:あるクラスについて、インスタンスが1つしか存在しないことを保証する
◆構造に関するパターン
・Adapter:元々関連性のない2つのクラスを接続するクラスを作る
・Bridge:クラスと呼び出し側の間の橋渡しをするクラスで、実装を隠蔽する
・Composite:再帰的な構造を表現する
・Decorator:あるインスタンスに対し、動的に付加機能を追加する
・Facade:複数のサブシステムの窓口となる共通のインタフェースを提供する
・Flyweight:多数のインスタンスを共有し、インスタンスの構築のための負荷を減らす
・Proxy:共通のインタフェースを持つインスタンスを内包し、利用者からのアクセスを代理する。Wrapperとも呼ばれる
◆振る舞いに関するパターン
・Chain of Responsibility:イベントの送受信を行う複数のオブジェクトを鎖状につなぎ、それらの間をイベントが渡されていくようにする
・Command:複数の異なる操作について、それぞれに対応するオブジェクトを用意し、オブジェクトを切り替えることで、操作の切り替えを実現する
・Interpreter:構文解析のために、文法規則を反映するクラス構造を作る
・Iterator:複数の要素を内包するオブジェクトのすべての要素に対して、順番にアクセスする方法を提供する
・Mediator:オブジェクト間の相互作用を仲介するオブジェクトを定義し、オブジェクト間の結合度を低くする
・Memento:データ構造に対する一連の操作のそれぞれを記録しておき、以前の状態の復帰または操作の再現が行えるようにする
・Observer:インスタンスの変化を他のインスタンスから監視できるようにする
・State:オブジェクトの状態を変化させることで、処理内容を変えられるようにする
・Strategy:データ構造に対して適用する一連のアルゴリズムをカプセル化し、アルゴリズムの切り替えを容易にする
・Template Method:アルゴリズムは抽象クラスで、処理内容はサブクラスで定義する
・Visitor:データ構造を保持するクラスと、それに対して処理を行うクラスを分離する
今回は、「Template Methodパターン」をやっていきます!
サンプルコードの理解
Template Methodパターンは、処理の流れは抽象クラスで定義しておき、各処理の具体的な内容は、それぞれのサブクラスで実装するデザインパターンです。
分かりやすい例がありました。
Template Method パターン - Wikipedia
文章を書く流れを、ヘッダ、アイテム(ボディ)、フッタと、抽象クラスで定義しておきます。
普通の文章のサブクラスでは、ヘッダ、アイテム(ボディ)、フッタを、通常の文章として、箇条書きに書いています。一方、HTMLのサブクラスでは、同じ流れを、HTMLの箇条書きの構文を使用して書いています。
処理の流れは同じだけど、それぞれの処理の内容が少しずつ違うような実装の場合に役立ちそうです。
今回のPlantUMLのクラス図
PlantUMLで作成したクラス図のコードと画像ファイルです。
@startuml abstract class AbstractDisplay { + {abstract} void open() + {abstract} void print() + {abstract} void close() + final void display() } class CharDisplay { - char ch + void open() + void print() + void close() } AbstractDisplay <|-- CharDisplay class StringDisplay { - String string - int width - void printLine() + void open() + void print() + void close() } AbstractDisplay <|-- StringDisplay @enduml

Mainクラス(使う側のクラス)
まずは、Template Methodパターンどう使われるのかを見るために、Mainクラスを見ていきます。
分かりやすくなると思うので、先に実行結果も示します。
public class Main { public static void main(String[] args) { // 'H'を持ったCharDisplayのインスタンスを1個作る AbstractDisplay d1 = new CharDisplay('H'); // "Hello, world."を持ったStringDisplayのインスタンスを1個作る AbstractDisplay d2 = new StringDisplay("Hello, world."); // d1,d2とも、すべて同じAbstractDisplayのサブクラスのインスタンスだから // 継承したdisplayメソッドを呼び出すことができる // 実際の動作は個々のクラスCharDisplayやStringDisplayで定まる d1.display(); d2.display(); } }
実行結果

Template Methodのクラスは、AbstractDisplay です。
ソースコードのコメントにもありますが、処理の流れは、AbstractDisplay の display() で定義されていて、各処理(下にソースを示します)は、CharDisplay と StringDisplay で、それぞれ定義されているようです。
これだけでは分からないので、それぞれのソースコードを見ていきます。
AbstractDisplayクラス
Template Methodのクラスの AbstractDisplay です。
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(); } }
抽象クラスが定める処理の流れは、display() ですね。最初に open() をコールして、次は、print() を5回コールし、最後は、close() をコールしています。
display() は、'final'(オーバーライドできない)としているところが重要なようです。おそらく、もし、ここをサブクラスで上書きできてしまうと、サブクラスをさらに継承した孫クラスを作り、ここで display() に対して、処理順を変えてしまうような上書きをしてしまうと、正しく動かないものになってしまうからだと思います。
open()、print()、close() は、サブクラスで定義しているようです。
CharDisplayクラス
次は、CharDisplayクラスです。
public class CharDisplay extends AbstractDisplay { private char ch; // 表示すべき文字 // コンストラクタ public CharDisplay(char ch) { this.ch = ch; } @Override public void open() { // 開始文字列として"<<"を表示する System.out.print("<<"); } @Override public void print() { // フィールドに保存しておいた文字を1回表示する System.out.print(ch); } @Override public void close() { // 終了文字列として">>"を表示する System.out.println(">>"); } }
プライベートメンバ変数として、ch を持ち、コンストラクタでは、引数で ch を初期化しています。
open() では << を標準出力に表示し(改行は出力しない)、print() では ch を表示します。最後に、close() では >> を出力しています(改行も出力する)。
Mainクラスで、CharDisplayクラスのインスタンスを作るときに、引数に H を指定していました。よって、実行結果の通り、<<HHHHH>> と表示されたわけですね。なるほど。
StringDisplayクラス
次は、StringDisplayクラスです。
public class StringDisplay extends AbstractDisplay { private String string; // 表示すべき文字列 private int width; // 文字列の表示幅 // コンストラクタ public StringDisplay(String 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.println("+"); } }
プライベートメンバ変数として、string と width を持ち、コンストラクタでは、引数で string を初期化し、width はその長さで初期化しています。
open() と close() は同じ実装になっており、+ を出力した後、string と同じ長さの - を出力しています。print() では | の後、string を表示し、最後に、| を表示しています。
Mainクラスで、StringDisplayクラスのインスタンスを作るときに、引数に Hello, world. を指定していました。よって、実行結果の通り、以下と表示されたわけです。
+++++++++++++++ |Hello, world.| |Hello, world.| |Hello, world.| |Hello, world.| |Hello, world.| +++++++++++++++
おわりに
今回はTemplate Methodパターンを学びました。
似たような処理の流れのクラスは、Template Methodパターンを用いると、同じような実装が存在することを防ぐことができますね。また、先に処理の流れが決まっている場合に、間違いなく、それを守ることが実現できそうです。
今回は以上です!
最後までお読みいただき、ありがとうございました。