新人SEの学習記録

14年度入社SEの学習記録用に始めたブログです。気づけば社会人3年目に突入。

学習記録:デザインパターン

[学習記録] デザインパターン

第7章:Builderパターン

概要
  • 構造を持つ大きなものを建築・構築することをbuildという
    • 全体の構成している各部分を作り、段階を踏んで組み上げていく
    • 構造を持ったインスタンスを積み上げていくのがBuilderパターン
サンプルプログラム
  • Builderパターンを使って文書を作成する
    • 文書とは以下のような構造を持つ
    • タイトルを1つ含む
    • 見出しをいくつか含む
    • 1つの見出しにつき本文を1つ含む
Builder 文書を構成するためのメソッドを定めた抽象クラス
Director 1つの文書を作るクラス
TextBuilder プレーンテキストの文書を作るクラス
HTMLBuilder HTML文書を作るクラス
Main 動作テスト用
  • Builderクラス
    • 文書を作るメソッドを定義する抽象クラス
    • それぞれタイトル、見出し、本文を構築するメソッドに、closeで文書を完成させる
public abstract class Builder {
	public abstract void makeTitle(String title);
	public abstract void makeHeadline(String head);
	public abstract void makeBody(String body);
	public abstract void close();
}
public class Director {
	private Builder builder;
	
	public Director(Builder builder) {
		this.builder = builder;
	}
	
	public void construct() {
		builder.makeTitle("文書タイトル");
		for (int i = 1; i <= 3; i++) {
			builder.makeHeadline("見出し" + i);
			builder.makeBody("本文" + i);
		}
		builder.close();
	}
}
  • TextBuilder
    • プレーンテキストの文書を作成
public class TextBuilder extends Builder {
	private StringBuffer buffer = new StringBuffer();
	
	@Override
	public void makeTitle(String title) {
		buffer.append("======================\n");
		buffer.append("『").append(title).append("』\n\n");
	}

	@Override
	public void makeHeadline(String head) {
		buffer.append("■").append(head).append("\n");
	}

	@Override
	public void makeBody(String body) {
		buffer.append(body).append("\n\n");
	}

	@Override
	public void close() {
		buffer.append("=====================\n");
	}
	
	public String getResult() {
		return buffer.toString();
	}
}
  • HTMLBuilder
    • HTMLの文書を構築
public class HTMLBuilder extends Builder {
	private String filename;
	private PrintWriter writer;
	
	@Override
	public void makeTitle(String title) {
		filename = title + ".html";
		try {
			writer = new PrintWriter(new FileWriter(filename));
		} catch  (IOException e) {
			e.printStackTrace();
		}
		writer.println("<html><head><title>" + title + "</title></head><body>");
		writer.println("<h1>" + title + "</h1>");
	}

	@Override
	public void makeHeadline(String head) {
		writer.println("<h2>" + head + "</h2>");
	}

	@Override
	public void makeBody(String body) {
		writer.println("<p>" + body + "</p>");
	}

	@Override
	public void close() {
		writer.println("</body></html>");
                writer.close();
	}
	
	public String getResult() {
		return filename;
	}
}
  • Main
public class Main {

	public static void main(String[] args) {
		TextBuilder textBuilder = new TextBuilder();
		Director director = new Director(textBuilder);
		director.construct();
		System.out.println(textBuilder.getResult());
		
		HTMLBuilder htmlBuilder = new HTMLBuilder();
		director = new Director(htmlBuilder);
		director.construct();
		System.out.println(htmlBuilder.getResult() + "が生成されました");
	}
}
======================
『文書タイトル』

■見出し1
本文1

■見出し2
本文2

■見出し3
本文3

=====================

文書タイトル.htmlが生成されました


% cat 文書タイトル.html
<html><head><title>文書タイトル</title></head><body>
<h1>文書タイトル</h1>
<h2>見出し1</h2>
<p>本文1</p>
<h2>見出し2</h2>
<p>本文2</p>
<h2>見出し3</h2>
<p>本文3</p>
</body></html>
登場人物
  • Builder
  • ConcreteBuilder
    • Builderのインタフェースを実装しているクラス
    • 実際のインスタンス生成で呼び出されるメソッドがここで定義される
    • また、最終的に結果を得るためのメソッドも用意される
  • Director
    • Builderのインタフェースを使ってインスタンス生成を行う
    • ConcreteBuilderに依存したプログラミングは行わない
    • ConcreteBuilderが何であっても機能するように、Builderのメソッドのみを使用する
  • Client
    • Builderパターンを利用する役
補足とまとめ
  • 関連しているパターン
  • 誰が何を知っているか
    • どのクラスがどのメソッドを使えるかに注意してプログラミングを行う必要がある
    • MainクラスはBuilderクラスのメソッドを知らないし、呼び出さない。
    • DirectorはBuilderクラスを知っているが、実際に利用しているBuilderクラスのサブクラスについては知らない
  • Builderクラスは、文書の構築に必要かつ十分なメソッド群を宣言している必要がある
    • 将来増えるかもしれないBuilderクラスのサブクラスたちの要求にも応える必要がある
    • 全てを見通すことはできないにしても、クラス設計者は予想される変化には耐えられるように設計を行う必要がある
  • ソースの読み方と修正の仕方
    • プログラミングの多くの場合は既にできているソースへの修正や追加
    • 既存のソースを読む時、抽象クラスだけを眺めていても情報は増えない
    • サンプルの例だと、Builderだけを読んでいてもらちがあかない
    • Direcotorのソースを読み、Builderの使い方を理解し、その上でText/HTMLBuilderを呼んで動作を確認する
    • クラスの役割を理解していないと、修正や追加で誤りを犯す