python-设计模式-创建型

348 阅读4分钟

运行环境

  • 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