blog

python-design-patterns-creation-type

ナルトは一度に多くのドッペルゲンガーを作ることができ、それぞれのドッペルゲンガーは本体からチャクラを消耗します。 本体が大きなダメージを受けると、ドッペルゲンガーは全て消滅。 ドッペルゲンガーが消滅し...

May 7, 2020 · 6 min. read
シェア

動作環境

  • 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

Read next

5つの一般的なメソッドによるjsトラバーサルクエリ

find メソッドのコールバック関数は 3 つの引数を取ることができ、順に現在の値、現在の...

May 7, 2020 · 3 min read