Shoeisha Technology Media

CodeZine(コードジン)

特集ページ一覧

デザインパターンの使い方: Builder

インターフェイスと実装の分離を実現するBuilderパターン

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2008/09/30 14:00

 Builderパターンは、Template Methodパターンによく似ています。Template Methodパターンとの大きな違いは、その目的がオブジェクトを生成することであり、アルゴリズムを実行することではないという点です。Builderパターンは、役割分担を明確にするだけでなく、インターフェイスと実装の分離を実現する代表的な事例であり、パターンを使用しない場合に起こりうる重複をかなりの程度防ぐことができます。

目次

Builderパターンの例

 図書館の所蔵目録(カタログ)は、書籍や映画などさまざまな資料の集合体です(リスト1を参照)。図書館ではいろいろなレポートを頻繁に印字しますが、まず考えられるのは、館内のすべての資料を一覧にして種類ごとにソートした所蔵資料レポートです(きっと相当に膨大なレポートになるでしょう)。

リスト1 基本的な資料クラス
// Material.java:
public class Material {
   private String classification;
   private String title;
   private String director;
   private String year;

   public Material(String classification, String title,
                   String director, String year) {
      this.classification = classification;
      this.title = title;
      this.director = director;
      this.year = year;
   }

   public String getAuthor() {
      return director;
   }

   public String getClassification() {
      return classification;
   }

   public String getTitle() {
      return title;
   }

   public String getYear() {
      return year;
   }
}

// Book.java:
public class Book extends Material {
   public Book(String classification, String title,
               String author, String year) {
      super(classification, title, author, year);
   }
   // ...
}

// Movie.java:
public class Movie extends Material {
   public Movie(String classification, String title,
                String director, String year) {
      super(classification, title, director, year);
   }

   public String getDirector() {
      return getAuthor();
   }
   // ...
}

 Catalogクラスは、各種資料のシンプルなコンテナです。リスト2をご覧ください。

リスト2: Catalogクラス
package builder;

import java.util.*;

public class Catalog implements Iterable<Material> {
   private List<Material> materials = new ArrayList<Material>();

   public void add(Material material) {
      materials.add(material);
   }

   public List<Material> materials() {
      return materials;
   }

   @Override
   public Iterator<Material> iterator() {
      return materials.iterator();
   }
}

 レポートはシンプルな生成プロセスを使用して作られます。はじめに、資料の一覧を種類ごとにグループ化して(たとえば、最初に書籍、次に映画のように)ソートします。レポートには適切なヘッダーを印字します。次に、ソートした所蔵資料の一覧全体について繰り返し処理を行い、各行を出力していきます。最後に、レポートの末尾に適切な補足情報を印字します。

 リスト生成の各ステップの詳細は状況により変わってきます。たとえば、Webレポートでは適切なHTMLタグが必要です。あるいは、横幅に制約のあるプリントアウト(明細書など)では、おそらくいくつかのフィールドの割愛や、アウトプットの再レイアウトが必要でしょう。

 Builderパターンは、こうしたレポート作成機能の設計を取りまとめるための理想的な手段を提供します。Builderパターンでは、クライアントがオブジェクトの種類と内容を指定するという方法で複雑なオブジェクトの生成を制御します。このクライアントは「ディレクター(director)」と呼ばれ、実装の詳細を一切指定しません。実装の詳細は別のビルダー階層の担当になります。

 リスト3に、ディレクタークラスを示します。ディレクターオブジェクトの生成アルゴリズムは、generateメソッドの1行として表現されます。つまり、まずCatalogオブジェクトをsort関数でソートし、それを適切なビルダーオブジェクトと共にcreateReportメソッドに引き渡します。createReport内のコードでは、ビルダーオブジェクトへのコールバックを何度か行います。具体的には、builderに対してまずヘッダーの生成を要求し、次に各資料に対応するオブジェクトの詳細なアウトプットの生成を要求し、最後にフッターの生成を要求します。このcreateReportメソッドが生成アルゴリズムの「シェル」に該当します。

 最終的には、ビルダーがgetReportメソッドによってレポートを出力します。ディレクターは、このレポートを呼び出し側のクライアントへ返します。

リスト3 ディレクターの例
import java.util.*;

public class CatalogReportDirector {
   private final Catalog catalog;

   public CatalogReportDirector(Catalog catalog) {
      this.catalog = catalog;
   }

   public String generate(CatalogReportBuilder builder) {
      return createReport(builder, sort(catalog));
   }

   private List<Material> sort(Catalog catalog) {
      List<Material> catalogCopy =
         new ArrayList<Material>(catalog.materials());
      Collections.sort(catalogCopy, new Comparator<Material>() {
         @Override
         public int compare(Material material1,
                            Material material2) {
            if (material1.getClass() == material2.getClass()) {
               if (material1.getTitle().
                  equals(material2.getTitle()))
                  return material1.getAuthor().
                     compareTo(material2.getAuthor());
               return material1.getTitle().
                  compareTo(material2.getTitle());
            }
            return material1.getClass().getName().compareTo(
                  material2.getClass().getName());
         }
      });
      return catalogCopy;
   }

   private String createReport(CatalogReportBuilder builder,
         List<Material> sortedCatalog) {
      builder.generateHeader();
      for (Material material: sortedCatalog)
         builder.generateDetail(material);
      builder.generateSummary();
      return builder.getReport();
   }
}

  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • japan.internet.com(ジャパンインターネットコム)

    japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.com や EarthWeb.c...

  • Jeff Langr(Jeff Langr)

    本格的なソフトウェアの開発に四半世紀以上携わってきたベテランのソフトウェア開発者。『Agile Java: Crafting Code With Test-Driven Development』(Prentice Hall、2005年)と、他の1冊の著書がある。『Clean Code』(Uncle...

バックナンバー

連載:デザインパターンの使い方

もっと読む

All contents copyright © 2005-2019 Shoeisha Co., Ltd. All rights reserved. ver.1.5