SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

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

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

 CatalogReportFactoryで定義されている各抽象メソッドは、関連する一群のファクトリを保持するためのプレースホルダーです。CatalogReportFactoryの各実装は、見出し、明細レコード、および要約セクションを生成できるファクトリの実装を提供するために必要です。リスト6は、HTML形式のレポートを生成するファクトリの実装です(興味があるなら、リスト7のMaterialSorterのコードをご覧ください)。

リスト6 HtmlCatalogReportFactory
import domain.*;

public class HtmlCatalogReportFactory extends CatalogReportFactory {
   public HtmlCatalogReportFactory(Catalog catalog) {
      super(catalog);
   }

   @Override
   protected ReportComponent createDetailRecord(Material material) {
      return new HtmlReportDetailRecord(material);
   }

   @Override
   protected ReportComponent createReportHeader() {
      return new HtmlReportHeader();
   }

   @Override
   protected ReportComponent createSummary() {
      return new HtmlReportSummary();
   }
}
リスト7 MaterialSorter
import java.util.*;
import domain.*;

public class MaterialSorter implements Comparator<Material> {
   private List<Material> materialsCopy;

   public MaterialSorter(List<Material> materials) {
      materialsCopy = new ArrayList<Material>(materials);
   }

   @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());
   }

   public List<Material> sort() {
      Collections.sort(materialsCopy, this);
      return materialsCopy;
   }
}

 それぞれの生成メソッドは次に示すReportComponentを返します。

public interface ReportComponent {
   void appendTo(Report report);
}

 ReportComponentの仕事は、レポート(Report)に付加できるだけの分量のテキストを生成することです。Reportはクライアントのコードを簡素化するための便利な要素がいくつか追加された簡単なヘルパークラス(StringBuilderのラッパー)です。

public class Report {
   static final String EOL = System.getProperty("line.separator");
   private StringBuilder buffer = new StringBuilder();

   @Override
   public String toString() {
      return buffer.toString();
   }

   public void appendLine(String text) {
      buffer.append(text);
      buffer.append(EOL);
   }
}

 HTMLレポートのコンポーネントを生成する各ファクトリは、大部分がごく簡単な1行のメソッドです。リスト8に、それらのうちの3つの実装とHtmlユーティリティクラスを示します。

リスト8 HTMLファクトリコンポーネント
// Html.java
public class Html {
   public static String td(String element) {
      return tag(element, "td");
   }

   public static String th(String element) {
      return tag(element, "th");
   }

   public static String tag(String element, String tag) {
      return String.format("<%s>%s</%s>", tag, element, tag);
   }
}

// HtmlReportHeader.java
import static abstractfactory.Html.*;

public class HtmlReportHeader implements ReportComponent {
   @Override
   public void appendTo(Report report) {
      report.appendLine("<body>");
      report.appendLine("<table>");
      report.appendLine(String.format("<tr>%s%s%s%s</tr>",
                        th("Title"), th("Auth/Dir"),
                        th("Classification"), th("Year")));

   }
}

// HtmlReportDetailRecord.java
import static abstractfactory.Html.*;
import domain.*;

public class HtmlReportDetailRecord implements ReportComponent {
   private final Material material;

   public HtmlReportDetailRecord(Material material) {
      this.material = material;
   }

   @Override
   public void appendTo(Report report) {
      report.appendLine(String.format("<tr>%s%s%s%s</tr>",
         td(material.getTitle()), td(material
         .getAuthor()), td(material.getClassification()),
         td(material.getYear())));
   }
}


// HtmlReportSummary.java
public class HtmlReportSummary implements ReportComponent {
   @Override
   public void appendTo(Report report) {
      report.appendLine("</table>");
      report.appendLine("</body>");
   }
}

 ReportComponentのこれらの実装はテストも簡単です! その上、レポートの種類が増えても、きわめて柔軟に対応できます。たとえば、HtmlReportSummaryの実装を使えば、どのようなHTMLレポートも生成できるでしょう。

 これはAbstract Factoryパターンの使い方として適切なのでしょうか? また、Builderよりもこちらを使った方がよいのでしょうか? 一概には決められませんが、今回の例に関して言うなら、多分そうです。これは種類に関する情報をクライアントから隠蔽したいという要求を満たしてくれます。今回の例で示したクライアントはレポートが、どこかのプラットフォームなのかそれともWebレポートなのかを示す目印をまだ渡す必要があります。この情報をプロパティのファイルに埋め込んで、Abstract Factoryから直接読み取るようにすることも簡単にできるでしょう。一方、Builderは段階的な構成を強調するように設計されていますが、この例のような目的ではそれは必要ありません。

 どのデザインパターンについても言えることですが、Abstract FactoryにはBuilderパターンとよく似た面があります。現実のニーズに合ったパターンを適用することが大切です。多くの場合、複数のパターンを組み合わせて実装することになるでしょう。

図1 Abstract Factoryパターン
図1 Abstract Factoryパターン

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
デザインパターンの使い方連載記事一覧

もっと読む

この記事の著者

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

japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.comEarthWeb.com からの最新記事を日本語に翻訳して掲載するとともに、日本独自のネットビジネス関連記事やレポートを配信。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

Jeff Langr(Jeff Langr)

本格的なソフトウェアの開発に四半世紀以上携わってきたベテランのソフトウェア開発者。『Agile Java: Crafting Code With Test-Driven Development』(Prentice Hall、2005年)と、他の1冊の著書がある。『Clean Code』(Uncle Bob Martin著、Prentice Hall、2008年8月)にも寄稿している。また、ソフトウェア開発に関する記事を80件以上執筆しており、そのうちの...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/3824 2009/04/28 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー