【Java】Template Methodパターン【デザインパターン】
目次
- Template Methodパターンについて
- Template Methodパターンとは
- Template Methodパターンのメリット
- LSP (The Liskov Substitution Principle) とは
- 今日のポイント
Template Methodパターンについて
Template Methodパターンとは
- スーパークラスで処理の枠組みを定め、サブクラスでその具体的内容を定めるデザインパターンです
- スーパークラスでは、テンプレートとなる抽象メソッドを定義します
- テンプレートとは、
文書などのコンピュータデータを作成する上で雛形となるデータ
- テンプレートとは、
- のことで、例えば、カリグラフィーなど文字を綺麗に書くためのテンプレート板などがあります
- テンプレート板を使えば、どんなペンでなぞっても同じデザインの文字を描くことができます
- オブジェクト指向言語では、同じような処理を1つのクラスとして纏めるように設計するので、Template Methodパターンに則っているということですね〜💡
サンプルプログラム
- Template Methodパターンを使って、文字や文字列を5回繰り返して表示する例を取り上げます
- 各クラスの役割は以下のようになっています
名前 | 役割 |
---|---|
AbstractDisplay (AbstractClass) | displayメソッドのみ実装されている抽象クラス |
CharDisplay (ConcreteClass) | open、print、closeを実装しているクラス 文字表示をする |
StringDisplay (ConcreteClass) | open、print、closeを実装しているクラス 文字列表示をする |
AbstractDisplayクラス
- 抽象クラスAbstractDisplayでは、 サブクラスに実装をまかせる抽象メソッド (open,print,close) を用意します
- AbstractDisplayクラスでは、displayメソッドを実装します
- まずopenして、5回printを繰り返して、最後にcloseします
- displayメソッドがテンプレートメソッドになります
public abstract class AbstractDisplay { public abstract void open(); public abstract void print(); public abstract void close(); public final void display() { open(); for (int i = 0; i < 5; i++) { print(); } close(); } }
CharDisplayクラス
- CharDisplayクラスは、AbstractDisplayのサブクラスです
- コンストラクタで渡された文字chを、フィールドに保存します
- スーパークラスの抽象メソッド (open,print,close) を、オーバーライドして実装します
- printメソッドでは、フィールドに記憶しておいた文字を1個表示します
- AbstractDisplayクラスのdisplayメソッドから繰り返して呼び出されます
- スーパークラスで宣言したメソッドをサブクラスで実装する際には、実装したメソッドが呼ばれるタイミングを理解しておきましょう
public class CharDisplay extends AbstractDisplay { private char ch; public CharDisplay(char ch) { this.ch = ch; } public void open() { System.out.print("<<"); } public void print() { System.out.print(ch); } public void close() { System.out.println(">>"); } }
StringDisplayクラス
- StringDisplayクラスは、AbstractDisplayのサブクラスです
- コンストラクタで渡された文字列stringと、バイト単位の幅widthを、フィールドに記憶します
- スーパークラスの抽象メソッド (open,print,close) を、オーバーライドして実装します
- open、closeメソッドでは、StringDisplayクラスのメソッドprintLineで線を引きます
- printメソッドでは、フィールドに記憶しておいた文字列の前後に"|"をつけて表示します
- printLineメソッドはprivateなので、このクラスの中だけで使われます
public class StringDisplay extends AbstractDisplay { private String string; // 表示するべき文字列 private int width; // バイト単位で計算した文字列の幅 public StringDisplay(String string) { this.string = string; this.width = string.getBytes().length; } public void open() { printLine(); } public void print() { System.out.println("|" + string + "|"); } public void close() { printLine(); } private void printLine() { System.out.print("+"); for (int i = 0; i < width; i++) { System.out.print("-"); } System.out.println("+"); } }
Mainクラスで動作確認
- CharDisplay、StringDisplayのインスタンスを1個作ります
- 作成したインスタンス(d1,d2,d3)は、AbstractDisplayのサブクラスのインスタンスなので、 継承したdisplayメソッドを呼び出すことができます
- 実際の動作は個々のクラスCharDisplayやStringDisplayで定まります
public class Main { public static void main(String[] args) { AbstractDisplay d1 = new CharDisplay('H'); AbstractDisplay d2 = new StringDisplay("Hello, world."); AbstractDisplay d3 = new StringDisplay("こんにちは。"); d1.display(); d2.display(); d3.display(); } }
Template Methodパターンのメリット
- スーパークラスのテンプレートメソッドでアルゴリズムが記述されているので、サブクラスではアルゴリズムを記述しなくて良いです
- アルゴリズムがが各クラスに散らばっていないので、テンプレートメソッドにバグがあった場合に改修がしやすいです
- スーパークラス型の変数に、サブクラスのインスタンスが代入されているので、サブクラスの種類を特定しなくても動くコードになります
LSP (The Liskov Substitution Principle) とは
- クラス同士の継承関係が正しいか、を検証する原則で、リスコフの置換原則ともいわれます
- Template Methodパターンに限らない、継承の一般的な原則です
- 具体的には、スーパークラスとサブクラスを定義するとき、サブクラス別のサブクラスに置き換えても、スーパークラスがは正常に動作しないといけないというルールです
- 継承関係でバグが発生しないように実装するときに気をつけるべきは、以下の点です
今日のポイント
- Template Methodパターンは、スーパークラスで処理の枠組みを定め、サブクラスでその具体的内容を定めるデザインパターンです
- 同じような流れの処理を共通化したい時に、Template Methodパターンを適応することができます
- スーパークラス(抽象クラス)では、 サブクラスに実装をまかせる抽象メソッド を用意します
- スーパークラス型の変数に 、サブクラスのインスタンスを代入することで、サブクラスの種類を特定しなくても動くコードになります
- よって、リスコフの置換原則に則った設計ができているということになります
本日もお疲れ様です😊