KnowHow

技術的なメモを中心にまとめます。
検索にて調べることができます。

Python基礎 オブジェクト指向7(Prototype) 生成に関するデザインパターン

登録日 :2025/09/27 21:13
カテゴリ :Python基礎

Prototypeとは

原型となるインスタンスをコピーして新しいインスタンスを生成するパターン

親クラスでインスタンスをコピーするためのメソッドを定義し、子クラスで自分自身のコピーを返すように実装する

生成に関するデザインパターン

浅いコピー・深いコピー

浅いコピー

  • 変数に格納された値がそのままコピーされる
  • オブジェクトの参照を格納した変数では、参照がコピーされる
    =>どちらかのオブジェクトを変更すると、もう片方も変更される

深いコピー

  • オブジェクトの参照を格納した変数では、実体がコピーされる
    =>どちらかのオブジェクトを変更しても、もう片方に影響を与えない
    ただし、デメリットとしてコピーするときにメモリやCPUなどの負荷がかかることに注意

構成要素

Manager

  • コピーするオブジェクトを管理しつつ、インスタンスを要求された際にコピーを返す

Prototype

  • インターフェースもしくは抽象クラス
  • インスタンスをコピーして新しいインスタンスを作るためのメソッドを定める

ConcretePrototype

  • Prototypeを実装(継承)する子クラス
  • Prototypeで定義されたコピー用のメソッドを実装する

オブジェクト指向的要素

「継承」と「ポリモーフィズム」を利用したパターン

  • PrototypekeyとConcretePrototypeクラスの間に継承(実装)関係
  • Managerは親クラスであるPrototypeクラスで提供されているAPIのみを使ってプログラミングを行う
  • ManagerとConcretePrototypeに依存関係がないので、ConcretePrototypeの差し替えや追加。修正が可能

メリット・デメリット

メリット

  • オブジェクトの生成処理を隠蔽できる
  • 構築済みのプロトタイプのクローンの作成を使うことにより、初期化コードの重複を削減
  • 利用者と具体的なクラスの結合度を弱められる

デメリット

  • 浅いコピー(sharrow copy)と深いコピー(deep copy)を意識していないと、想定外のバグを生む可能性がある。

使い所

  • クラスからのインスタンス生成が難しい場合
    ユーザー操作によって生成されたグラフィックオブジェクトなど
    例)パワーポイントの図形のコピー

  • インスタンス化のコストがコピーよりも高い場合

サンプルコード

from __future__ import annotations
import copy
from abc import ABCMeta, abstractmethod
from typing import List, Union


class ItemPrototype(metaclass=ABCMeta):
    def __init__(self, _name: str):
        self.__name = _name
        self.__review: List[str] = []

    def __str__(self):
        return f"{self.__name}: {self.__review}"

    def set_review(self, _review: str):
        self.__review.append(_review)

    @abstractmethod
    def create_copy(self) -> ItemPrototype:
        pass


class DeepCopyItem(ItemPrototype):
    def create_copy(self) -> ItemPrototype:
        return copy.deepcopy(self)


class ShallowCopyItem(ItemPrototype):
    def create_copy(self) -> ItemPrototype:
        return copy.copy(self)


class ItemManager(object):
    def __init__(self):
        self.items = {}

    def register_item(self, _key: str, _item: ItemPrototype):
        self.items[_key] = _item

    def create(self, _key: str) -> Union[ItemPrototype, None]:
        if _key in self.items:
            item = self.items[_key]
            return item.create_copy()
        raise Exception("指定されたKeyが存在しません")


if __name__ == '__main__':
    mouse = DeepCopyItem('Mouse')
    Keyboard = ShallowCopyItem('Keyboard')

    manager = ItemManager()
    manager.register_item("mouse", mouse)
    manager.register_item("keyboard", Keyboard)

    cloned_mouse = manager.create("mouse")
    cloned_keyboard = manager.create("keyboard")

    cloned_mouse.set_review("Good!")
    cloned_keyboard.set_review("SoSo!")

    print("")
    print("mouse(original):", mouse)
    print("mouse(copy):", cloned_mouse)
    print("")
    print("keyboard(original)", Keyboard)
    print("keyboard(copy)", cloned_keyboard)