Flyweight パターン

多数のオブジェクト間で共有可能な状態を分離し、メモリ使用量を削減するデザインパターン

設計パターンメモリ管理

Flyweight パターンとは

Flyweight パターンは、多数のオブジェクトが共有できる状態 (内部状態) と、オブジェクト固有の状態 (外部状態) を分離し、内部状態を共有することでメモリ使用量を削減する GoF デザインパターンである。数千〜数百万のオブジェクトを扱う場合に効果的だ。

内部状態と外部状態

内部状態と外部状態を以下にまとめる。

状態説明共有例 (テキストエディタ)
内部状態 (Intrinsic)オブジェクト間で共有可能するフォント、サイズ、色
外部状態 (Extrinsic)オブジェクト固有しない文字の位置 (x, y)

実装例: テキストレンダリング

実装例: テキストレンダリングのコード例を示す。

// Flyweight: 共有される内部状態
class CharacterStyle {
  constructor(
    readonly font: string,
    readonly size: number,
    readonly color: string,
  ) {}
}

// Flyweight Factory: 同じスタイルを共有
class StyleFactory {
  private cache = new Map<string, CharacterStyle>();

  getStyle(font: string, size: number, color: string): CharacterStyle {
    const key = `${font}-${size}-${color}`;
    if (!this.cache.has(key)) {
      this.cache.set(key, new CharacterStyle(font, size, color));
    }
    return this.cache.get(key)!;
  }
}

// 使用: 100万文字でもスタイルオブジェクトは数十個だけ
const factory = new StyleFactory();
const characters = text.split('').map((char, i) => ({
  char,
  x: i * 10,
  y: 0,
  style: factory.getStyle('Arial', 14, '#000'),  // 共有
}));

100 万文字のドキュメントでも、フォントスタイルの組み合わせは数十種類程度だ。Flyweight なしでは 100 万個のスタイルオブジェクトが作られるが、Flyweight では数十個で済む。

言語ランタイムに組み込まれた Flyweight

Java の Integer キャッシュは、言語仕様が保証する Flyweight だ。Java 言語仕様 5.1.7 (boxing 変換) は、boxing する値が定数式の結果で、かつ -128〜127 の int (ほかに boolean と '\u0000'〜'\u007f' の char) なら、2 回の boxing 結果 a と b について常に a == b が成り立つ、つまり同じオブジェクトになることを要求している。頻出する狭い範囲の値だけを共有する典型的な Flyweight である。

Integer a = 127, b = 127;
System.out.println(a == b);  // true (仕様が同一オブジェクトを保証)

Integer c = 128, d = 128;
System.out.println(c == d);  // 保証の範囲外 (多くの実装では false)

JavaScript の文字列も、エンジンによっては同じリテラルを内部で 1 つに集約する。ただしこれを Flyweight の実例として示すのは避けたい。=== は文字列の中身だけを見る比較 (同じ文字が同じ順に並んでいれば真) なので、'hello' === 'hello' が true になっても共有の証拠にはならず、共有されているかどうかを JavaScript のコードから観測する手段が無いためだ。

Object Pool パターンとの違い

Object Pool パターンとの違いを以下にまとめる。

パターン目的オブジェクトの状態
Flyweightメモリの節約不変 (共有して読み取り専用)
Object Pool生成コストの回避可変 (使用後にリセット)

Flyweight は不変オブジェクトを共有する。Object Pool は可変オブジェクトを再利用する。

実務での活用

  • ゲーム開発: 同じテクスチャやスプライトを共有し、座標や向きは各インスタンスが持つ
  • コネクションプール: プール自体は Object Pool だが、各接続が参照する接続設定 (ホスト、認証情報、タイムアウト) は不変なので共有できる
  • CSS クラス: スタイル定義を内部状態、要素ごとの位置や内容を外部状態として切り離す構造で、考え方は Flyweight と同じだ

詳しくは関連書籍を参照。

この記事は役に立ちましたか?

関連用語

関連する記事