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)
詳しくは関連書籍を参照。