ビルダーオブジェクトの作成とディレクターへの引き渡しをクライアントが担当しなければならないという点が、Builderパターンのもう1つの特徴です。
では、ビルダーはどのような実装になるのでしょうか? リスト4に、抽象クラスとして作成されたビルダーの構造の一例を示します。リスト5は、HTMLビルダーの実装です。
public abstract class CatalogReportBuilder {
static final String EOL = System.getProperty("line.separator");
private StringBuilder report = new StringBuilder();
public String getReport() {
return report.toString();
}
abstract public void generateHeader();
abstract public void generateDetail(Material material);
abstract public void generateSummary();
protected void appendLine(String text) {
report.append(text);
report.append(EOL);
}
}
このビルダークラスはreportへの参照を保持しています。生成アルゴリズムの部分を実行する際に、このreportに内容が書き込まれます。リスト6に、また別のビルダーの例を示します。
public class HtmlCatalogBuilder extends CatalogReportBuilder {
@Override
public void generateDetail(Material material) {
appendLine(String.format("<tr>%s%s%s%s</tr>",
td(material.getTitle()),
td(material.getAuthor()),
td(material.getClassification()),
td(material.getYear())));
}
@Override
public void generateHeader() {
appendLine("<body>");
appendLine("<table>");
appendLine(String.format("<tr>%s%s%s%s</tr>",
th("Title"),
th("Auth/Dir"),
th("Classification"),
th("Year")));
}
@Override
public void generateSummary() {
appendLine("</table>");
appendLine("</body>");
}
private String td(String element) {
return tag(element, "td");
}
private String th(String element) {
return tag(element, "th");
}
private String tag(String element, String tag) {
return String.format("<%s>%s</%s>", tag, element, tag);
}
}
public class PrintCatalogBuilder extends CatalogReportBuilder {
private static final String ROW_LAYOUT = "%s %s %s %s";
private static final int YEAR_WIDTH = 4;
private static final int CLASSIFICATION_WIDTH = 14;
private static final int AUTHOR_WIDTH = 20;
private static final int TITLE_WIDTH = 24;
@Override
public void generateDetail(Material material) {
appendLine(format(
material.getTitle(),
material.getAuthor(),
material.getClassification(),
material.getYear()));
}
@Override
public void generateHeader() {
String header = format("Title", "Auth/Dir",
"Classification", "Year");
appendLine(header);
appendLine(replaceWithDashes(header));
}
private String replaceWithDashes(String header) {
return header.replaceAll("\\S", "-");
}
@Override
public void generateSummary() {
appendLine("");
}
private String format(
String title, String author, String classification,
String year) {
return String.format(ROW_LAYOUT,
pad(title, TITLE_WIDTH),
pad(author, AUTHOR_WIDTH),
pad(classification, CLASSIFICATION_WIDTH),
pad(year, YEAR_WIDTH));
}
private String pad(String text, int width) {
if (text.length() >= width)
return text.substring(0, width);
StringBuilder spaces = new StringBuilder(text);
for (int i = 0; i < width - text.length(); i++)
spaces.append(' ');
return spaces.toString();
}
}
Builderパターンは、Template Methodパターンによく似ています。Template Methodパターンを使用する場合は、アルゴリズムの構造と、そのアルゴリズムの実装に必要な細かい共通事項をベースクラスで定義します。それを拡張クラスで「穴埋め」することによって、アルゴリズムを完成させます。BuilderパターンがTemplate Methodパターンと大きく異なるのは、その目的がオブジェクトを生成することであり、アルゴリズムを実行することではないという点です。Builderパターンは「分離型」のTemplate Methodパターンとも考えられます。アルゴリズムの定義は1つのクラス(ディレクター)で行われますが、実装は別のビルダー階層にて行われます。
Builderパターンは、役割分担を明確にするだけでなく、インターフェイスと実装の分離を実現する代表的な事例です。また、パターンを利用することで、パターンを使用しない場合に起こりうる重複をかなりの程度防ぐことができます。

