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 と同じだ
詳しくは関連書籍を参照。
この記事は役に立ちましたか?
関連用語
デザインパターン
ソフトウェア設計で繰り返し現れる問題に対する再利用可能な解決策のカタログ
オブジェクトプールパターン
生成コストの高いオブジェクトを事前に確保してプールし、再利用することでパフォーマンスを向上させるパターン
メモ化
関数の計算結果をキャッシュし、同じ引数での再計算を避ける最適化手法
State パターン
オブジェクトの内部状態に応じて振る舞いを切り替え、状態遷移を明示的にモデル化するデザインパターン
S3 ライフサイクル
S3 オブジェクトのストレージクラスを自動的に移行し、コストを最適化する機能
エンティティと値オブジェクト
DDD における 2 つの基本的なドメインモデル要素 - 同一性で区別するエンティティと、値で区別する値オブジェクト