概要
Flyweight(フライウェイト)パターンは、
多数のオブジェクトが類似または同一の状態を持つ場合に、
共有可能な部分(不変な内部状態)を外部に分離・再利用することで、メモリ効率を最適化する構造的パターンである。
数百万単位で生成されるオブジェクト群の状態の重複を排除し、構造的に共有することで軽量化を実現する。
1. なぜFlyweightが必要か?
❌ 同一状態のオブジェクトを大量に生成
for _ in range(1000000):
glyphs.append(Character("A", "Arial", 12))
→ メモリを浪費し、スケーラビリティが著しく低下
✅ 共有可能な状態を外部化し、Flyweightで再利用
factory.get_char("A", "Arial", 12)
→ 同一状態のインスタンスは1つだけ保持され、以後は共有参照
2. 基本構造
✅ Flyweightクラス(共有される軽量オブジェクト)
class Character:
def __init__(self, char, font, size):
self.char = char # 内部状態(共有)
self.font = font
self.size = size
def render(self, position):
print(f"{self.char} [{self.font}, {self.size}] at {position}")
✅ FlyweightFactory(インスタンスのキャッシュ管理)
class CharacterFactory:
def __init__(self):
self._pool = {}
def get(self, char, font, size):
key = (char, font, size)
if key not in self._pool:
self._pool[key] = Character(char, font, size)
return self._pool[key]
✅ 使用例
factory = CharacterFactory()
doc = []
for i in range(100):
doc.append(factory.get("A", "Arial", 12))
# 実際に生成されたCharacterインスタンスは1つのみ
print(len(set(id(c) for c in doc))) # → 1
3. Python的応用:@lru_cache
によるFlyweight化
from functools import lru_cache
@lru_cache(maxsize=128)
def get_color(r, g, b):
return (r, g, b)
color1 = get_color(255, 255, 255)
color2 = get_color(255, 255, 255)
assert color1 is color2 # 共有されている
→ 不変オブジェクトのキャッシュにより、実質Flyweight化
4. 実務ユースケース
✅ ゲームにおけるキャラクター表示・粒子・地形要素
→ 同一グラフィック・属性を持つエンティティを効率よくレンダリング
✅ フォントレンダリング・文字表示システム
→ 文字ごとにオブジェクトを作らず、キャッシュで共有レンダリング
✅ 色・スタイル・アイコンの共通化
→ UI上の装飾情報を構造的に管理・再利用
✅ ファイルパーミッション・属性・ステータスの定義共有
→ 状態をクラス化し、同一内容を共通オブジェクトで表現
5. よくある誤用と対策
❌ 外部状態と内部状態の区別が曖昧
→ ✅ 共有可能な状態(フォント・サイズなど)は内部に、
位置や一時的な情報は外部で持つ
❌ 共有すべきでないオブジェクトまでキャッシュする
→ ✅ 状態が変化するものはFlyweightに適さない
❌ キャッシュが肥大化し、逆にメモリ消費が増大
→ ✅ maxsize
を明示的に設定し、キャッシュ管理を明確に
結語
Flyweightパターンとは、“重複するデータを構造的に共有し、無駄をそぎ落とす設計技法”である。
- 高頻度・大量オブジェクト生成時に、最小限のリソースで最大の表現を可能に
- 内部状態と外部状態を分離し、オブジェクトの意味的重複を排除
- Pythonでは
dict
や@lru_cache
、オブジェクトIDの比較で、直感的な実装が可能
Pythonicとは、“同じものは使い回す”という合理性に基づいた設計であり、
Flyweightパターンはその節制と効率性を、メモリに染み込ませるための技術である。