概要
prototypeパターンは、オブジェクトのインスタンスを新たに作成するのではなく、クラス(プロトタイプ)のインスタンスを1つだけ作成し、必要に応じてディープコピーを行う。このパターンは、以下のような場合に特に有効。
- 初期化のコストが高い
- 同じタイプのオブジェクトが多数必要だが、設定にコスト(時間・スペース・帯域幅)がかかるいくつか/すべてのプロパティが、すべてのオブジェクトで同じ
以下の例では、ユーザのNoteのプロトタイプのユースケースを考えます。
任意のインデックスのすべてのNoteには、クエリや後処理に時間がかかる可能性のある普遍的なデータがあると仮定する。また各Noteはユーザに属しており、そのユーザに固有のデータが含まれていて、普遍的なデータが入力された後に入力できると仮定する。
NotePrototypeは1つの Index に1つのインスタンス(その Index のブリーダー)しか必要ないと考えられるため、パターンの良い使用例だ。
特定のユーザのためのNoteを作成するには、そのユーザに設定されたIndexに関連付けられた各ブリーダーのクローンを1つ作成するだけでよい。
実装例
NoteFactoryクラスは、実装例のためのクラスでprototypeパターンを非常によく補完する。プロトタイプのすべてのインスタンスはこのクラスに含まれる。これらは、make()
メソッドを使ってインターフェイスにすることができる。このクラスは、シングルトンやキャッシュモデルなど、別の方法で実装することができる。
Userクラスは、実装例のためのクラスでprototypeパターンには関係ない。インスタンス化するだけでNoteを生成する。
import copy
class NotePrototype:
def __init__(self, index):
self.index = index
self.note = None
self.user_id = None
self._build_general_note()
def set_user(self, id):
self.user_id = id
self._populate_user_note()
def _build_general_note(self):
"""
この関数はコストがかかることを想定している。できる限り呼び出さない
"""
pass
def _populate_user_note(self):
"""
データを入力するもので、set_userからのみ呼び出されるべき。
クローンごとのコストのかかる計算やクエリはすべてここで処理すべき。
"""
pass
def clone(self):
# 独自のIDとプロパティを持つ全く新しいオブジェクトとして複製
return copy.deepcopy(self)
def __repr__(self):
return "<Note: user_id: {}, index: {}>".format(self.user_id,
self.index)
class NoteFactory():
_note_breeders = {}
def __init__(self):
pass
def make(self, id, index):
if index not in NoteFactory._note_breeders:
NoteFactory._note_breeders[index] = NotePrototype(index)
clone = NoteFactory._note_breeders[index].clone()
clone.set_user(id)
return clone
class User():
def __init__(self, id, indices, note_factory):
self.id = id
self.indices = indices
self.notes = []
self.note_factory = note_factory
self._get_notes()
def _get_notes(self):
for index in self.indices:
note = self.note_factory.make(self.id, index)
self.notes.append(note)
if __name__ == "__main__":
factory = NoteFactory()
user_0 = User(id=0, indices=[0, 1, 2], note_factory=factory)
user_1 = User(id=1, indices=[1, 2, 3], note_factory=factory)
print(user_0.notes)
print(user_1.notes)