2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Pythonで構築するFlyweightパターン:大量オブジェクトを軽量に扱う共有設計

Posted at

概要

Flyweight(フライウェイト)パターンは、
大量に生成される同一・類似オブジェクトの内部状態を共有化することで、
メモリ使用量と生成コストを最適化する設計手法
である。

主に、ゲーム開発、テキストレンダリング、マップシステムなど、
「構造は異なるが構成要素が重複する」ようなケースで高い効果を発揮する。


1. なぜFlyweightが必要か?

❌ 大量の重複データを持つオブジェクトを都度生成

class Tree:
    def __init__(self, type_name, texture, height):
        self.type_name = type_name
        self.texture = texture
        self.height = height

→ 数千個のツリーが存在すると、テクスチャや種別情報が何度も複製されて非効率


✅ 共通部分(内部状態)を共有し、個別状態のみを保持

# TreeType: 共有される内部状態
# Tree: 個別の座標など外部状態を持つ

インスタンスの数を論理的に保ちつつ、実メモリを削減


2. 基本構造

✅ Flyweightクラス(内部状態)

class TreeType:
    def __init__(self, name, color, texture):
        self.name = name
        self.color = color
        self.texture = texture

    def draw(self, x, y):
        print(f"Draw [{self.name}] at ({x}, {y})")

✅ FactoryでFlyweightを管理

class TreeFactory:
    _types = {}

    @classmethod
    def get_tree_type(cls, name, color, texture):
        key = (name, color, texture)
        if key not in cls._types:
            cls._types[key] = TreeType(name, color, texture)
        return cls._types[key]

✅ 個別オブジェクト(外部状態のみ)

class Tree:
    def __init__(self, x, y, tree_type):
        self.x = x
        self.y = y
        self.type = tree_type

    def draw(self):
        self.type.draw(self.x, self.y)

✅ 使用例:数千本の木を効率的に生成

forest = []
for i in range(10000):
    ttype = TreeFactory.get_tree_type("Oak", "Green", "oak_texture.png")
    forest.append(Tree(i % 100, i // 100, ttype))

forest[0].draw()  # Draw [Oak] at (0, 0)

TreeType は1つ、Tree は10000個でも メモリ負荷を劇的に低減


3. Python的応用:文字オブジェクトの共有化

class CharFlyweight:
    _pool = {}

    @classmethod
    def get(cls, char):
        if char not in cls._pool:
            cls._pool[char] = char
        return cls._pool[char]

text = [CharFlyweight.get(c) for c in "hello world" * 1000]

同一文字オブジェクトを共有することで、メモリ節約


4. 実務ユースケース

✅ テキストエディタでの文字レンダリング

→ フォントや文字ごとのスタイルを共有化し、軽量化


✅ ゲーム内オブジェクト(敵キャラ、背景、木々など)

→ 外見情報は共有し、位置・ライフなどは個別に管理


✅ マップ/地図情報の構造物パターンの最適化

→ 同一建物タイプなどを共有し、構造の再利用を促進


5. よくある誤用と対策

❌ 全てを共有して状態が干渉する

→ ✅ 外部状態(mutableな位置やスコアなど)は絶対に個別管理


❌ キャッシュが肥大化しメモリリーク

→ ✅ Factoryにキャッシュ上限や削除ルール(LRU)を実装


❌ Flyweightを意識しすぎて設計が複雑に

→ ✅ 共有メリットが明確な場面に限定し、オーバーエンジニアリングを回避


結語

Flyweightパターンとは、“無限に広がる構造を有限な部品で支える”設計戦略である。

  • 重複した情報を構造的に共有し、メモリ効率とスケーラビリティを両立
  • 表現は多様でも、本質的に「同じ」ものを共通化し、構成要素として再定義する
  • Pythonにおいても、クラス設計とFactoryの工夫で柔軟かつ軽量な構造化が可能

Pythonicとは、“不要な重複を設計で消す”ことであり、
Flyweightパターンはその消去と抽出のアートを具現化するパターンである。

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?