動作環境
- python >= 3.7
作成パターンは、オブジェクトがどのようにクラスを作成するかという問題に対する一般的な解決策です。
シングルトン例
シナリオ: 商品の追加と削除ができるショッピングカートをデザインしてください。
制限事項:ソフトウェアのライフサイクルの中で唯一無二のもの
class Product:
def __init__(self, name: str, price: float, count: float):
self.name = name
self.price = price
self.count = count
@property
def amount():
return self.price * self.count
def __eq__(self, other):
return self.name == other.name
class Cart:
_instance = None
def __new__(cls):
if not cls._instance:
cls._instace = super().__new__(cls)
return cls._instance
def __init__(self):
self.products: list[Product] = []
def add_product(self, product: Product):
self.products.append(product)
def remove_product(self, product: Product):
self.products.remove(product)
def settle():
return sum([product.amount() for product in self.products])
assert Cart() is Cart()
シングルトンをまとめると、解決すべき問題は、オブジェクトをグローバル参照として
実際、オブジェクトへのアクセスポイントを提供しさえすれば、シングルトンと呼ぶことができます。
例えば、python の構文的な性質はさておき、コンストラクタは
_instance = None
class Product:
def __init__(self, name, price, count):
self.name = name
self.price = price
self.count = count
@property
def amount():
return self.price * self.count
def __eq__(self, other):
return self.name == other.name
class Cart:
def __init__(self):
self.products: list[Product] = []
def add_product(self, product: Product):
self.products.append(product)
def remove_product(self, product: Product):
self.products.remove(product)
def settle():
return sum([product.amount() for product in self.products])
def get_cart():
global _instance
if not _instance:
_instace = Cart()
return _instance
assert get_cart() is get_cart()
原形
シーン:ナルトのシャドウ・ドッペルゲンガーのデザイン
制限:作成は、ある時点におけるコピーされたオブジェクトの状態に依存します。
class Ninja:
def __init__(self, name, blood=100, chakra=100):
self.name = name
self.blood = blood
self.chakra = chakra
def shadow_clone(self):
return Ninjia(self.name, self.blood, self.chakra)
naruto = Ninja(' ')
naruto_copy = naruto.shadow_clone()
assert naruto is not naruto_copy
assert naruto.name == naruto_copy.name
assert naruto.blood == naruto_copy.blood
assert naruto.chakra == naruto_copy.chakra
プロトタイプパターンは、特定のインスタンスの現在の状態によってコピーすることができます。
そんなことはないですよ:
- ナルトは一度にたくさんのドッペルゲンガーを出すことができ、それぞれのドッペルゲンガーが本体からチャクラを消耗します。
- 本体が大きなダメージを受けると、ドッペルゲンガーは消滅します。
- ドッペルゲンガーがいなくなると、ドッペルゲンガーの記憶やトラウマが本体に戻ってくる・・・。
考えすぎて、時間的な制約があると、ここでは実現できません。
工場
簡易植物
シナリオ: 点、線、多角形のグラフをデシリアライズします。
限界:グラフの拡張性を考慮する必要性
class Shape:
pass
class Polygon(Shape):
def __init__(self, center, width, height):
self.center = center
self.width = width
self.height = height
class Circle(Shape):
def __init__(self, center, radius):
self.center = center
self.radius = radius
class ShapeFactory:
@classmethod
def create_shape(cls, data: dict) -> Shape:
if data['type'] == 'polygon':
shape = Polygon(
data['shape']['center'],
data['shape']['width'],
data['shape']['height'])
elif data['type'] == 'circle':
shape = Circle(
data['shape']['center'],
data['shape']['radius'])
else:
raise Exception()
return shape
p = ShapeFactory.create_shape({'type': 'polygon', 'shape': {
'center': [30, 30], 'width': 15, 'height': 15}})
c = ShapeFactory.create_shape({'type': 'circle', 'shape': {
'center': [30, 30], 'radius': 15}})
assert p.center == [30, 30]
assert p.width == 15
assert p.height == 15
assert c.center == [30, 30]
assert c.radius == 15
ShapeFactory.create_shapeメソッドは、グラフィック・クラスが追加されるたびに変更しなければならないためです。
しかし、pythonの動的言語機能を使えば、これを補うことができます!
class Shape:
type_ = None
class Polygon(Shape):
type_ = 'polygon'
def __init__(self, center, width, height):
self.center = center
self.width = width
self.height = height
class Circle(Shape):
type_ = 'circle'
def __init__(self, center, radius):
self.center = center
self.radius = radius
import inspect
shape_classes = {v.type_: v for _, v in locals().items() if inspect.isclass(v) and issubclass(v, Shape) and v is not Shape}
assert shape_classes == {'polygon': Polygon, 'circle': Circle}
class ShapeFactory:
@classmethod
def create_shape(cls, data: dict) -> Shape:
shape_type = data['type']
if shape_type not in shape_classes:
raise Exception()
return shape_classes[shape_type](**data['shape'])
p = ShapeFactory.create_shape({'type': 'polygon', 'shape': {
'center': [30, 30], 'width': 15, 'height': 15}})
c = ShapeFactory.create_shape({'type': 'circle', 'shape': {
'center': [30, 30], 'radius': 15}})
assert p.center == [30, 30]
assert p.width == 15
assert p.height == 15
assert c.center == [30, 30]
assert c.radius == 15
抽象化ファクトリー
シーン:着替え自由
制限:着せ替えの追加や着せ替えの自由度の検討
class Clothing:
def __init__(self, name):
self.name = name
class Shoes(Clothing):
pass
class Hat(Clothing):
pass
class Coat(Clothing):
pass
class LeatherShoes(Shoes): #
pass
class CanvasShoes(Shoes): #
pass
class BaseballHat(Hat): #
pass
class TopHat(Hat): #
pass
class TShirt(Coat): # T
pass
class Suit(Coat): #
pass
class Style:
@property
def shoes(self):
raise NotImplementedError
@property
def coat(self):
raise NotImplementedError
@property
def hat(self):
raise NotImplementedError
class ShangWu(Style):
@property
def shoes(self):
return LeatherShoes(' ')
@property
def coat(self):
return Suit(' ')
@property
def hat(self):
return TopHat(' ')
class XiuXian(Style):
@property
def shoes(self):
return CanvasShoes(' ')
@property
def coat(self):
return TShirt('T ')
@property
def hat(self):
return BaseballHat(' ')
class Person:
def __init__(self, style=None):
self._style = style
@property
def style(self) -> Style:
return self._style
@style.setter
def style(self, st: Style):
self._style = st
@property
def shoes(self):
return self.style.shoes
@property
def coat(self):
return self.style.coat
@property
def hat(self):
return self.style.hat
sw = ShangWu()
xx = XiuXian()
p = Person()
p.style = sw
assert p.shoes.name == ' '
assert p.coat.name == ' '
assert p.hat.name == ' '
p.style = xx
assert p.shoes.name == ' '
assert p.coat.name == 'T '
assert p.hat.name == ' '
ビルダー
シナリオ:ハイディラオのアラカルトソースのシミュレーション
限界:絶対的に自由に素材を選択できること
class Jiangliao:
def __init__(self):
# :
self.items = {
'zhimajiang': 0,
'hongyou': 0,
'zhimaxiangyou': 0,
'huasheng': 0
}
def add_zhimajiang(self, count):
self.items['zhimajiang'] += count
def add_hongyou(self, count):
self.items['hongyou'] += count
def add_zhimaxiangyou(self, count):
self.items['zhimaxiangyou'] += count
def add_huasheng(self, count):
self.items['huasheng'] += count
jl = Jiangliao()
jl.add_zhimajiang(10)
jl.add_zhimaxiangyou(3)
jl.add_huasheng(3)
jl.add_hongyou(3)
jl.add_zhimajiang(1)
assert jl.items['zhimajiang'] == 11
assert jl.items['zhimaxiangyou'] == 3
assert jl.items['huasheng'] == 3
assert jl.items['hongyou'] == 3
ビルダーのアプローチは、ビルドをユニットビルドのステップに分割し、ユニットビルドを自由に組み合わせてビルド全体を完成させることです。
前提条件として、ビルドの各ステップは互いに無関係で、依存しあってはいけません。
分かち合う
ソフトウェア・アーキテクチャ:Python言語の実装、236-162
デザインパターン:再利用可能なオブジェクト指向ソフトウェアの基礎, 54-89