跳到内容

设计模式(创建型)

Canyue
发布日期:
4 分钟阅读

设计模式(Design Pattern)是软件开发中经过验证的最佳实践,用于解决在软件设计中常见的问题。

经典设计模式可分为三种:

类型关注点
创建型(本章节)对象怎么来
结构型对象怎么组装
行为型对象间怎么协作

工厂方法

定义一个用于创建对象的接口,让子类决定实例化那个类

你是鹿幸咖啡的市场部门,经调研决定,每家门店在原有咖啡品类的产品上,集合当地特殊推出城市限定品类:

分店做的咖啡
北京店老北京豆汁拿铁
福州店茉莉花茶美式

工厂模式核心价值体现在,总部(CoffeeShop)无需关注所有地方店铺的细节,每开家新地方店,就不需要修改总部的代码

# 不然你要这么写
class CoffeeShop:
    def localOrder(self, city: str):
        if city == "北京":
            coffee = BeanJuiceLatteCoffee()          # 硬编码
        elif city == "福州":
            coffee = JasmineTeaAmericanoCoffee()
        # 每开一家新店,就要改这个类

抽象工厂

相比前者,不是在造一个东西,而是造”一整套”东西,而且这套东西的风格必须一致,不能混搭。

你去买家具,店里按风格分成了两个区:

现代简约风中式复古风
沙发灰色布艺、细金属腿红木雕花
茶几钢化玻璃、极简实木榫卯、带雕花

你可以选现代风,也可以选复古风,但不能混搭。客厅里放一张红木沙发配北欧风极简茶几,会非常难看。

当然,你乐意你随意

相较于前者,抽象工厂的特点就是,把”配套”这件事交给工厂,客户端永远拿不到混搭的结果。

工厂方法抽象工厂
管几个产品一个产品(一个等级结构)一族产品(多个等级结构)
工厂的样子一个工厂一个方法,造一种东西一个工厂一堆方法,造一整套
约束力只管”怎么造出来”还要管”造出来的东西能不能配在一起”
类比单点菜单:每家店只卖汉堡,口味不同套餐:汉堡+薯条+可乐,必须成套出
本例如果退化create_sofa() 单独抽出去 → 工厂方法三个 create_xxx 合在一个工厂里 → 抽象工厂

想加一个新风格(比如”叙利亚风”),只需新建 Factory + 三个对应产品类,客户端和老代码一行不改

生成器/创造者

对象的构建与表示分离,使同样的构建过程可以创建不同的表示

一份三明治可以分为三部分,面包、馅料、酱汁,但无论选择哪种组合,做三明治的过程简单且一致

如果使用构造函数:

Sandwich("法棍", "芥末", "金枪鱼")   # 你认得出来那个参数是那个配料

使用生成器创建对象,一切就变得更加直观、优雅:

sandwich.bread("法棍").filling("金枪鱼").sauce("芥末").build()

类图很简单,如下:

class SandwichBuilder:
    def __init__(self):
        self._s = Sandwich()

    def bread(self, name):
        self._s.bread = name
        return self            # 返回自己,才能链式调用

    def filling(self, name):
        self._s.filling = name
        return self

    def sauce(self, name):
        self._s.sauce = name
        return self

    def build(self):
        s = self._s
        self._s = Sandwich()   # 重置,不然下一份会串味
        return s

生成器模式其实非常适合超多属性对象的创建

原型

不想从头造一个,就照着现成的复制一份,改改细节接着用。

你是名应届毕业生,海投了100家公司,每家的简历内容 95% 一样,只有”应聘岗位”和”自我评价”两处不同。

你不会每次都重新写一遍,而是:写好一份底稿 → 修改→分别打印修改的版本

这份底稿就是原型(Prototype),每次打印出来的就是克隆(clone)

就不画类图了,一个类就能实现:

class Resume:

    def __init__(self, name: str):
        self.name = name
        self.age = 23
        self.education = "XX大学 · 计算机 · 本科"
        self.experience = ["A公司后端开发3年", "B公司架构师2年"]
        self.target_job = ""      # 每家不一样
        self.self_eval = ""       # 每家不一样

    def clone(self):
        """克隆:照着自己复制一份"""
        return copy.deepcopy(self)   # 关键:深拷贝,一个新对象

master = Resume("张三")
        
job_a = master.clone()
job_a.target_job = "Java后端工程师"
job_a.self_eval = "熟悉高并发,抗压能力强"

job_b = master.clone()
job_b.target_job = "系统架构师"
job_b.self_eval = "有从0到1搭建微服务的经验"

原型模式主要解决的问题:有些对象创建成本很高(查数据库、读配置、噼里啪啦算半天),但它大部分状态是可以复用的,那就先造一个”样板”,后面全靠复制。

单例

一个类的实例,全世界只能有一个

一家公司可以有无数份文件、无数个员工、无数台电脑,但公章只有一枚

也只需要一个类就能演示,就不画图了:

class Seal:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self):
        # 注意:__init__ 每次都会被调用!真正初始化要加保护(见下面的坑)
        self.owner = "XX科技有限公司"
        self.code = "1101080000001"


# 验证:三个人拿到的都是同一枚章
zhang_san = Seal()
li_si = Seal()
wang_wu = Seal()

print(zhang_san is li_si is wang_wu)   # True
print(id(zhang_san), id(li_si), id(wang_wu))  # 三个数字完全一样

当然,实现方式多种多样,像什么饿汉式、懒汉式,挖坑后续更


参考资料:

《软件设计师教程》 - 清华大学出版社

《您的设计模式》 - 设计模式公司

一些AI给出的建议

下一篇
设计模式(结构型)