117
90

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

はじめに

本投稿はJava言語で学ぶデザインパターン入門のデザインパターンをまとめた記事です。今回はSingletonパターンになります。
まとめ一覧はこちら

Template Methodパターン

スクリーンショット 2016-02-14 15.31.29.png

Templateとは

Wikipedia参照

文書などのコンピュータデータを作成する上で雛形となるデータ

とありますが、本書では文字を綺麗に書くためのテンプレート板を例に紹介されています。

異なる色のペンや、筆・クレヨンなどを用いても同じ形の文字になるように、スーパークラスで処理の枠組みを決め、サブクラスで具体的な内容を定めるデザインパターンをTemplate Methodパターンと呼びます。

手続き型プログラミング言語は似た処理を一つの関数にまとめるのに対し、オブジェクト指向言語では、自然と似た処理の部分を1つのクラスとしてまとめていると思います。この設計はまさにTemplate Methodパターンに準拠していますね。

似たような流れの処理を共通化したい時にこのパターンを適応することが多いです。

Template Methodパターンのクラス図

スクリーンショット 2016-02-14 15.53.14.png

  • AbstractClassで抽象メソッドの宣言、テンプレートメソッドの実装
  • ConcreteClassで抽象メソッドの実装

具体例

文字・文字列を3回繰り返し表示する簡単な例で紹介していきます

Mainクラス

クラスの生成と一緒に渡した文字(列)が、加工されて表示されるようなクラスを作っていきます。

Main.java
public static void main(String[] args) {
		
        //※
		AbstractDisplay cd = new CharDisplay('T');
		AbstractDisplay sd = new StringDisplay("Design Pattern");
		AbstractDisplay sd2 = new StringDisplay("Template Method");
		
		cd.display();
		sd.display();
		sd2.display();	
}

異なるサブクラスの生成をスーパークラスに代入しています。
詳しくはLSP参照(後述)

AbstractClass

文字・文字列を3回繰り返し表示するTemplate Methodを実装しています

AbstractDisplay.java
public abstract class AbstractDisplay {
	public abstract void open();
	public abstract void print();
	public abstract void close();
    //Template Method
	public final void display() {
		open();
		for (int i =0; i < 3; i++) {
			print();
		}
		close();
	}
}

ConcreteClass

CharDisplayクラス

  • 文字の前後に***を付与
CharDisplay.java
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.java
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("+");
	}

}

実行結果

***TTT***
+--------------+
|Design Pattern|
|Design Pattern|
|Design Pattern|
+--------------+
+---------------+
|Template Method|
|Template Method|
|Template Method|
+---------------+

このようにスーパクラスのTemplateMethodのロジックで、処理の共通化ができてますね。

追記

  • メリット

  • スーパークラスにロジックが集中しているので、バグが生じてもTemplateMethodのみを修正すればいい

  • サブクラスの設計が簡潔になる

  • デメリット

  • サブクラスの数が増加する

  • 親と子の関係が密接なので、スーパークラスのソースを確認しないと実装が難しい

  • スーパークラスの処理が大きくなると、サブクラスの自由度が減少する

LSP(The Liskov Substitution Principle)

オブジェクト指向プログラミングにおける派生型の定義の一種で、リスコフの置換原則ともいわれます。
簡単に言うとTemplate Methodのように、スーパークラスとサブクラスを定義するとき、サブクラスはスーパークラスを置き換えることができなければならないという原則です。

Aクラスに依存するBクラスが存在するとする。
BクラスをのサブクラスB', やさらにサブクラスB''に置き換えても正常の動作しなければならないということです。

例えば...

サブクラスでオーバーライドされた結果、動作が意図しないものに変わってはならない
・公開する必要がないメンバのアクセス修飾子は、privateにする
・オーバーライドをする必要がなければ、final修飾子のような宣言をする(Java)

結果がスーパークラスの意図から外れてしまうようなオーバーライドはしない

クラスAはサブクラスB'やB"を判定して処理をするような設計であってはならない

まとめると

  1. 同じスーパークラス(インタフェース)を持っていて、違うオブジェクトでも同じように扱える
  2. オブジェクトによって動作は異なる
  3. クライアントからは同じ動作をしているように見えなければならない

の条件を守った設計であれば、継承関係でバグが発生しなくうまくいくということなんですね。

参考

サンプルコードについて

以下のレポジトリにソースコードをアップしてあります。

shoheiyokoyama/design-pattern_java

デザインパターン

参考文献

117
90
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
117
90

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?