弾幕シューティングエンジンが完成しました!
実は、私たちが作ってきたこのシステムには名前があります。Flyweight(フライウェイト)パターンです。

これまでの振り返り
シリーズを通して、私たちは以下のことを学びました:
| 回 | やったこと | 実はこれが… |
|---|---|---|
| 第1回 | 大量オブジェクトでメモリ問題発覚 | Flyweightが必要な状況 |
| 第2回 | 弾の「種類」と「位置」を分離 | 内部状態と外部状態の分離 |
| 第3回 | BulletFactoryでキャッシュ | FlyweightFactory |
| 第4回 | render($x, $y)で位置を渡す | 外部状態を渡す操作 |
| 第5回 | BattleFieldで統合 | Clientクラス |
Flyweightパターンとは
Flyweightパターンは、GoF(Gang of Four)が提唱した23のデザインパターンの一つで、構造パターンに分類されます。
Flyweight(フライウェイト)= 軽量級 ボクシングの階級名に由来。「軽い」オブジェクトを作る、という意味。
パターンの目的
大量のオブジェクトを効率的に管理するために、共有可能な状態を共有する。
私たちの弾幕シューティングでは:
- 1000発の弾を生成しても
BulletTypeオブジェクトは5種類だけ- メモリ使用量を大幅に削減!
パターンの構造
Flyweightパターンは、以下の要素で構成されます:
| 要素 | 役割 | 本シリーズでの実装 |
|---|---|---|
| Flyweight | 共有されるオブジェクトのインターフェース | BulletType クラス |
| ConcreteFlyweight | 共有される具体的なオブジェクト | 各弾種(circle, star, dot等) |
| FlyweightFactory | Flyweightオブジェクトを管理・提供 | BulletFactory クラス |
| Client | Flyweightを使用するクラス | BattleField クラス |
| 内部状態(Intrinsic State) | 共有可能な状態 | 形状、色、サイズ |
| 外部状態(Extrinsic State) | 共有できない状態 | 位置、速度 |
classDiagram
class BulletType {
-name: String
-char: String
-color: String
+render(x, y, screen)
}
class BulletFactory {
-_cache: Hash
-_definitions: Hash
+get(key): BulletType
+count(): Int
}
class BattleField {
-width: Int
-height: Int
-factory: BulletFactory
-bullets: Array
+spawn(type, x, y, vx, vy)
+update(frame)
+render(frame)
}
BulletFactory --> BulletType : creates/caches
BattleField --> BulletFactory : uses
BattleField --> BulletType : references
内部状態と外部状態
Flyweightパターンの核心は、状態の分離です。
内部状態(Intrinsic State)
- オブジェクト間で共有できる状態
- オブジェクト自身が保持する
- 変更されない(不変)
| |
外部状態(Extrinsic State)
- オブジェクトごとに異なる状態
- クライアント(BattleField)が管理する
- 使用時に渡される
| |
Prototypeパターンとの違い
「PerlとMooでモンスター軍団を量産してみよう」シリーズで学んだPrototypeパターンと比較してみましょう。
| 項目 | Flyweightパターン | Prototypeパターン |
|---|---|---|
| 目的 | メモリ効率化 | オブジェクト生成の効率化 |
| 方法 | 共有(同じインスタンスを参照) | 複製(コピーを作成) |
| オブジェクト数 | 少ない(共有するから) | 多い(コピーするから) |
| 独立性 | 低い(同じオブジェクトを参照) | 高い(各クローンは独立) |
| 変更 | 変更すると全体に影響 | 各クローンは独立して変更可能 |
| 用途 | 大量の類似オブジェクト | 生成コストが高いオブジェクト |
| |
適用場面
Flyweightパターンが有効な場面:
- 大量のオブジェクトが必要 - 弾幕シューティングの弾、テキストエディタの文字
- オブジェクトの状態を分離できる - 内部状態と外部状態に分けられる
- メモリが制約 - モバイルデバイス、組み込みシステム
- 多くのオブジェクトが同じ内部状態を持つ - 同じ種類の弾がたくさんある
実際の使用例
Flyweightパターンは、さまざまな場面で使われています:
| 分野 | 例 | 内部状態 | 外部状態 |
|---|---|---|---|
| テキストエディタ | 文字オブジェクト | フォント、サイズ | 位置 |
| ゲーム | 弾、パーティクル | 形状、色 | 位置、速度 |
| GUIフレームワーク | アイコン、画像 | 画像データ | 表示位置 |
| データベース | コネクションプール | 接続設定 | 使用状況 |
シリーズのまとめ
このシリーズで学んだことを振り返りましょう:
技術的な学び
- 内部状態と外部状態の分離 - メモリ効率化の基本
- FlyweightFactory - オブジェクトプールの管理
- 外部状態を渡す操作 - 共有オブジェクトの活用
//=演算子 - キャッシュ機構の簡潔な実装
デザインパターンの学び
- Flyweightパターン - 構造パターンの一つ
- Prototypeとの違い - 「共有」vs「複製」
- 適用場面の判断 - いつFlyweightを使うべきか
達成したこと
- 1000発の弾を5オブジェクトで管理
- メモリ使用量を60%以上削減
- 弾幕シューティングエンジンの完成
発展的なトピック
このシリーズの発展として、以下のパターンも学んでみてください:
| パターン | 関連性 |
|---|---|
| Compositeパターン | ツリー構造でFlyweightを組織化 |
| Singletonパターン | FlyweightFactoryをSingletonにする発展形 |
| Proxyパターン | Flyweightへのアクセス制御 |
おわりに
「PerlとMooで弾幕シューティングを作ってみよう」シリーズ、いかがでしたか?
弾幕シューティングという楽しいテーマを通じて、Flyweightパターンの本質を学びました。「1000発の弾を5オブジェクトで管理する」という驚きは、このパターンの威力を体感する最高の教材だったと思います。
ぜひ、自分のプロジェクトでもFlyweightパターンを活用してみてください!
今回の完成コード
最終回として、Flyweightパターンの構造を意識したコメント付きのコードを掲載します:
| |
実行結果:
| |
シリーズ完結!
お読みいただきありがとうございました。
