0
0

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(フライウェイト)パターンは、
多数のオブジェクトを効率よく扱うために、共通する状態(内部状態)を共有し、必要な差異(外部状態)のみを個別に保持する設計パターンである。

UI要素、文字オブジェクト、グラフィックス、粒子エンジンなど、「似たようなインスタンスを大量に扱う」場面で極めて有効


1. なぜFlyweightが必要か?

❌ 大量のオブジェクトが似たような情報を個別に保持すると、メモリが逼迫する

# 数十万個のツリーオブジェクトに、毎回「種別・色・形状」が含まれている

実質的に同じデータが膨大に複製される


✅ 共通データ(内部状態)は共有オブジェクトに集約し、個別データ(外部状態)のみを保持

tree_type = TreeType("oak", "green", "texture.png")
tree = Tree(x, y, tree_type)

内部状態の共有により、インスタンス数を抑えてメモリ効率を最適化


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"{self.name}{x},{y} に描画(色: {self.color} / テクスチャ: {self.texture}")

✅ Flyweight Factory(共有インスタンスの管理)

class TreeFactory:
    _tree_types = {}

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

✅ コンテキスト(外部状態)

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

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

✅ 使用例

forest = []

for i in range(10000):
    tree_type = TreeFactory.get_tree_type("oak", "green", "oak_texture.png")
    forest.append(Tree(i, i, tree_type))

# 表示例
forest[0].draw()
forest[-1].draw()

出力:

oak を 0,0 に描画(色: green / テクスチャ: oak_texture.png)  
oak を 9999,9999 に描画(色: green / テクスチャ: oak_texture.png)

3. Python的応用:functools.lru_cache を使った共有インスタンス管理

from functools import lru_cache

@lru_cache(maxsize=None)
def get_icon(name, size):
    print(f"{name} サイズ{size}のアイコンをロード")
    return f"{name}_{size}_icon"

# 使用
icon1 = get_icon("save", 32)
icon2 = get_icon("save", 32)  # キャッシュヒット

共通リソースの読み込み最適化としてもFlyweight的に使える


4. 実務ユースケース

✅ UIコンポーネントのスキン/アイコン共有

→ 同じアイコンを複数ボタンで使い回すことで軽量化


✅ 地図アプリのランドマークアイコン

→ 数千のマーカーに対して、共通のアイコンデータを共有


✅ ゲームにおけるエフェクト・弾・敵ユニットの粒子構造

プロトタイプ・タイプ情報を共有してパフォーマンス改善


✅ Webアプリケーションのキャッシュ済みテンプレート構造

テンプレートエンジンがHTMLパーツを再利用


5. よくある誤用と対策

❌ 外部状態まで共有してしまい、正しい描画や動作が崩れる

→ ✅ 外部状態(位置・IDなど)は個別オブジェクトで必ず管理する


❌ 共有オブジェクトの構築コストを軽視して都度生成

→ ✅ Flyweight Factoryを介してキャッシュと制御を徹底する


❌ 状態の変更が共有インスタンスに波及して副作用を引き起こす

→ ✅ Flyweightは原則イミュータブル設計とする


結語

Flyweightパターンとは、“重複を構造化し、メモリを共有知で最適化する設計”である。

  • 内部状態を集約・共有し、必要最小限のインスタンス数で効率的な構造を実現
  • 速度・メモリ・スケーラビリティの観点から、高負荷アプリにおける最適解
  • PythonではFactory・キャッシュ・構造分離により、明確で安全な共有ロジックが構築可能

Pythonicとは、“個を分け、共を活かすこと”。
Flyweightパターンはその最小性の知性を、設計の経済性として導き出す技法である。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?