SHOEISHA iD

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

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

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

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

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

Flyweightを活用するためのポイント

Timeオブジェクト

 予約支援のためのスケジューリングアプリケーションを作成します。このアプリケーションで使用するTimeクラスは、時間(24時間制の時計を想定)と分で表現された時刻をカプセル化します。

public class Time {
   private byte hour;
   private byte minute;

   public Time(byte hour, byte minute) {
      this.hour = hour;
      this.minute = minute;
   }

   public byte hour() {
      return hour;
   }

   public byte minute() {
      return minute;
   }

   public String toString() {
      return hour + ":"+ minute;
   }
}

 このアプリケーションでは、何百万件にも及ぶような膨大な数の予約をサポートする必要があるので、必要となるメモリが心配のタネです。ただし実際には、営業日の1件1件の予約の開始時刻は通常、正時きっかりか、または正時から15分間隔です(0分、15分、30分、45分の4種類)。Flyweightパターンを使用して一意なTimeインスタンスのみを格納するようにすれば、標準的なtimeオブジェクトの数を最大96個(24時間×4種類)まで減らすことができます。この予約アプリケーションはその他の時刻もサポートしますが、予測可能な使い方ができる部分では、Flyweightを使用するとオブジェクト生成とメモリ使用量が最小限に抑えられます。

 Flyweightを活用するためのカギは、ファクトリメソッドを使用してオブジェクトのインスタンス化を制御する点にあります。ファクトリメソッドの役割は単にオブジェクトを生成することであり、指定された入力条件に応じて適切な型のオブジェクトを返します。TimeFactoryクラスを設計して、簡単な単体テストを作成してみます。

import static org.junit.Assert.*;
import org.junit.*;

public class TimeFactoryTest {
   private static final byte HOUR = 23;
   private static final byte MINUTE = 59;

   @Test
   public void create() {
      Time time = TimeFactory.create(HOUR, MINUTE);
      assertEquals("23:59", time.toString());
   }
}

 このテストを通るコードは簡単です。

public class TimeFactory {
   public static Time create(byte hour, byte minute) {
      return new Time(hour, minute);
   }
}

 さらに、次のような別のテストを用意します。

@Test
public void reuseOfMemory() {
   Time time1 = TimeFactory.create(HOUR, MINUTE);
   Time time2 = TimeFactory.create(HOUR, MINUTE);
   assertSame(time1, time2);
}

 このテストで確認したいのは、同じ時間と分を表す2つのtimeオブジェクトを生成した場合、どちらもメモリ内では同じオブジェクトを指すということです。この例のJUnitのassertSame()メソッドは、次のように指定した場合と同じことです。

assertTrue(time1 == time2);

 前述のTimeFactoryクラスを使用した場合、create()メソッドは別々のTimeインスタンスを返すため、このテストは失敗します。reuseOfMemoryテストに合格するためには、TimeFactoryの実装を次のように変更します。

import java.util.*;

public class TimeFactory {
   private static Map<String,Time> times = new HashMap<String,Time>();
   public static Time create(byte hour, byte minute) {
      String key = hour + ":"+ minute;
      Time time = times.get(key);
      if (time == null) {
         time = new Time(hour, minute);
         times.put(key, time);
      }
      return time;
   }
}

 インスタンス化をファクトリのcreate()メソッドにカプセル化したので、Timeオブジェクトが生成されるタイミングを制御できます。まず最初に、時間と分を連結して時刻を表す文字列とし、一意のキーを作成します。このキーを使用して、timesという新しいHashMapからエントリを取り出します(エントリが存在する場合)。エントリが存在するときは、そのエントリが返されます。エントリが存在しないときは新しいTimeオブジェクトを生成し、作成したキーでオブジェクトをtimesに格納します。

 これで完成です。このように、Flyweightの実装は単純明快です。主な欠点は、次のような点です。

  • Timeオブジェクトのインスタンス化は、すべてファクトリ経由で行う必要があります。ファクトリでインスタンス化を行わない場合、Flyweightのメリットを失います。
  • ファクトリの使用には、追加のメソッド呼び出しと、ハッシュテーブル検索というオーバーヘッドがあります。Javaでは、ごくまれにこのことがパフォーマンス上の問題となります。

次のページ
Flyweightパターンの検証

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

デザインパターンの使い方連載記事一覧

もっと読む

この記事の著者

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/2915 2008/09/08 14:00

イベント

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

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

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

メールバックナンバー