运行环境
- 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风格的实现,并不一定需要这样实现,其实只要提供一个该对象的访问入口都可以称作为单例
比如抛开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并不满足开闭原则,因为每添加一个图形类就要修改一下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-261
设计模式:可复用面向对象软件基础, 54-89