🤖
デザインパターンを学ぶ #11 フライウェイト(Flyweight)
1. はじめに
今回は Flyweight(フライウェイト)パターン。
目的は、大量に生成されるオブジェクトの共通部分を共有し、メモリ使用量を減らすことです。
大量のオブジェクトを一つずつ完全に持つと重すぎるので、
変わらない部分は使い回し(共有)、変わる部分だけ個別に持つという発想です。
2. Flyweightとは?
- オブジェクトを「共有できる内部状態(intrinsic)」と
「個別に与える外部状態(extrinsic)」に分ける - 内部状態は 再利用するインスタンスに持たせ、
外部状態は 呼び出し側から渡す
3. 実装イメージ(PHP)
例:地図上に大量のマーカー(ピン)を表示する場合
- 色や形 → 共通(共有Flyweight)
- 座標 → 個別(外部状態として後から渡す)
<?php
// 共有される部分(色やアイコンなど)
class MarkerIcon {
public function __construct(
public string $color,
public string $shape
) {}
public function draw(int $x, int $y): void {
echo "Draw {$this->color}-{$this->shape} marker at ({$x}, {$y})\n";
}
}
// Flyweight Factory:既存インスタンスを使い回す
class MarkerIconFactory {
/** @var MarkerIcon[] */
private array $cache = [];
public function get(string $color, string $shape): MarkerIcon {
$key = $color . '-' . $shape;
if (!isset($this->cache[$key])) {
$this->cache[$key] = new MarkerIcon($color, $shape);
}
return $this->cache[$key];
}
}
// --- 利用 ---
$factory = new MarkerIconFactory();
// 1万個のマーカーを配置(色ごとに共有)
$coords = [[10,20], [11,21], [12,22]];
foreach ($coords as [$x, $y]) {
$icon = $factory->get('red', 'pin'); // 共有オブジェクト
$icon->draw($x, $y); // 外部状態(座標)を後から渡す
}
出力例:
Draw red-pin marker at (10, 20)
Draw red-pin marker at (11, 21)
Draw red-pin marker at (12, 22)
4. 利用シーン
- 地図やゲームでの 大量オブジェクト表示(タイル、弾丸、兵士、樹木など)
- フォントや文字オブジェクト(文字の形は共有、座標は個別)
- IDEの構文ノード(トークンの型情報を共有)
5. メリット
- メモリ使用量を大幅に削減できる
- オブジェクト生成コストを抑えられる
- 同一属性の変更を一括で反映できる(共有部分なので)
6. 注意点
- 内部状態と外部状態の切り分け設計が必要(どこまで共有するか)
- 実装が複雑になりやすい → 本当に大量に生成される場合だけ使う
7. 他パターンとの比較
- Prototype:複製で高速生成、共有ではない
- Singleton:インスタンス1個固定、Flyweightは同種を複数共有
- Proxy:アクセス制御や遅延生成が目的、Flyweightは共有による軽量化が目的
8. テスト観点
- 同じ属性で取得したオブジェクトが同一インスタンスか(
===で確認) - 共有部分の変更が全箇所に反映されるか
- メモリ使用量や生成回数が減っているか
9. まとめ
Flyweightは、共通部分を共有・個別部分を外部から渡すことで
大量オブジェクトを効率的に扱う構造パターンです。
Discussion