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 では数十個で済む。

JavaScript での Flyweight

JavaScript の文字列インターニングは Flyweight の一例だ。同じ文字列リテラルは同じメモリを参照する。

const a = 'hello';
const b = 'hello';
console.log(a === b); // true (同じメモリを参照)

Object Pool パターンとの違い

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

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

実務での活用

  • React の仮想 DOM: 同じ型のコンポーネントは内部的に共有される
  • ゲーム開発: 同じテクスチャやスプライトを共有
  • データベースのコネクションプール: 接続設定を共有
  • CSS クラス: 同じスタイルを複数要素で共有 (CSS 自体が Flyweight)

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

関連用語