SHOEISHA iD

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

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

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

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

  • X ポスト
  • このエントリーをはてなブックマークに追加

 Prototypeパターンでは、所蔵目録がプロトタイプオブジェクト(この場合はBookオブジェクト)に自分自身のクローンを返すよう要求するだけにします。つまり、すべての処理を行うのはBookクラスだということです。また、Movieタイプを追加することを考えると、このソリューションはポリモーフィックにすべきです。

 このプロトタイプの考え方を取り入れて、より堅牢にリファクタリングしたソリューションをリスト4(テストコード)とリスト5(本番用コード)に示します。リスト6は、Material、Book、Movieの各クラスを示しています。

リスト4 プロトタイプを実践するためのテスト
import static org.junit.Assert.*;

import java.util.*;
import org.junit.*;

public class CatalogTest {
   private Catalog catalog;

   @Before
   public void initialize() {
      catalog = new Catalog();
   }

   @Test
   public void isEmptyOnCreation() {
      assertEquals(0, catalog.size());
   }

   @Test
   public void addNewBook() {
      final Book book = new Book("QA123", "author", "title",
                                 "1999", 1);
      catalog.addNew(book);
      assertEquals(1, catalog.size());
      List<Material> materials = catalog.get("QA123");
      assertEquals(1, materials.size());
      assertSame(book, materials.get(0));
   }

   @Test(expected=NoExistingCopyException.class)
   public void addNewCopyThrowsIfNoExistingCopy() {
      catalog.addCopy("QA123.4");
   }

   @Test
   public void addCopyViaPrototype() {
      final Book book = new Book("QA123", "title", "author",
                                 "1999", 1);
      catalog.addNew(book);
      catalog.addCopy("QA123");

      assertEquals(2, catalog.size());
      assertMaterialCopies("QA123", 2, book);
   }

   @Test
   public void addMovieCopyViaPrototype() {
      final Movie movie = new Movie("DD890", "Shining", "Kubrick",
                                    "2006", 1, Movie.Format.DVD);
      catalog.addNew(movie);
      catalog.addCopy("DD890");

      assertEquals(2, catalog.size());
      assertMovieCopies("DD890", 2, movie);
   }

   private void assertMaterialCopies(String classification,
      int number, Material material) {
      List<Material> materials = catalog.get(classification);
      assertEquals(number, materials.size());

      for (int i = 0; i < number; i++) {
         Material copy = materials.get(i);
         assertEquals(material.getClass(), copy.getClass());
         assertEquals(material.getClassification(),
                      copy.getClassification());
         assertEquals(material.getAuthor(), copy.getAuthor());
         assertEquals(material.getTitle(), copy.getTitle());
         assertEquals(material.getYear(), copy.getYear());
         assertEquals(i + 1, copy.getCopyNumber());
      }
   }

   private void assertMovieCopies(String classification,
                                  int number, Movie material) {
      assertMaterialCopies(classification, number, material);
      for (Material copy: catalog.get(classification))
         assertEquals(material.getFormat(),
                      ((Movie)copy).getFormat());
   }
}
リスト5 Prototypeパターンを使用した所蔵目録
import java.util.*;

public class Catalog {
   private MultiMap<String, Material> materials =
      new MultiMap<String, Material>();

   public void addCopy(String classification) {
      List<Material> copies = materials.get(classification);
      if (copies.isEmpty())
         throw new NoExistingCopyException();

      ''materials.put(classification, copies.get(0).copy());''
   }

   public int size() {
      return materials.valuesSize();
   }

   public void addNew(Material material) {
      materials.put(material.getClassification(), material);
   }

   public List<Material> get(String classification) {
      return materials.get(classification);
   }
}
リスト6 Materialの階層
// Material.java
abstract public class Material {
   private final String title;
   private final String classification;
   private final String author;
   private final String year;
   private String branch;
   private int copyNumber;

   public Material(String classification, String title,
                   String author, String year, int copyNumber) {
      this.classification = classification;
      this.title = title;
      this.author = author;
      this.year = year;
      this.copyNumber = copyNumber;
      branch = "checked out";
   }

   abstract public Material copy();

   public String getAuthor() {
      return author;
   }

   public String getClassification() {
      return classification;
   }

   public String getTitle() {
      return title;
   }

   public String getYear() {
      return year;
   }

   public int getCopyNumber() {
      return copyNumber;
   }

   public String getBranch() {
      return branch;
   }
}

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

   public Book copy() {
      return new Book(getClassification(), getTitle(),
                      getAuthor(), getYear(),
                      getCopyNumber() + 1);
   }
}

// Movie.java
public class Movie extends Material {
   public static enum Format {
      DVD, BluRay, VHS
   }

   private Format format;

   public Movie(String classification, String title, String author,
         String year, int copyNumber, Format format) {
      super(classification, title, author, year, copyNumber);
      this.format = format;
   }

   public Format getFormat() {
      return format;
   }

   public Movie copy() {
      return new Movie(getClassification(), getTitle(),
                       getAuthor(), getYear(),
                       getCopyNumber() + 1, getFormat());
   }
}
※注意

 この実装では、Javaのcloneメソッドを使用していません。これは、現在のコンストラクションデザインに基づいて私が行ったデザイン上の選択です。cloneを実装する場合は、シャローコピーかディープコピーかの検討も含めて、このメソッドを使用することによる影響をすべて考慮に入れるようにしてください。このトピックについてはJoshua Bloch著の『Effective Java』が参考になるでしょう。

 Prototypeパターンの最も重要な点は、既存の資料に自分自身のコピーを返すように指示することで、コードではCatalogのaddCopyメソッドの太字で示した行(リスト5を参照)にあたります。この短いたった1行によって、Catalogは、あらゆる既存のMaterialまたはMaterialサブクラスの変更や、新しいMaterialサブクラスの追加などの影響を受けない、「自己完結」した存在になります。また、Prototypeパターンは、優れたオブジェクト指向デザインの持つその他の基本的な考え方にのっとって、ポリモーフィックであり、依存性逆転の原則と単一責務の原則に合致しています。

図1 Prototypeパターン
図1 Prototypeパターン

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

  • X ポスト
  • このエントリーをはてなブックマークに追加
デザインパターンの使い方連載記事一覧

もっと読む

この記事の著者

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件以上執筆しており、そのうちの...

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

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

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/3825 2009/04/27 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

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

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

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

メールバックナンバー

アクセスランキング

アクセスランキング