用Python实现设计模式——建造者模式和原型模式

前言

前两篇文章介绍了单例模式和工厂模式,这两种设计模式实际上都属于软件工程中的创建型模式(Creational Pattern)。维基百科有对这类模式的定义:

软件工程中,创建型模式 是处理对象 "对象 (计算机科学)")创建的设计模式 "设计模式 (计算机)"),试图根据实际情况使用合适的方式创建对象。基本的对象创建方式可能会导致设计上的问题,或增加设计的复杂度。创建型模式通过以某种方式控制对象的创建来解决问题。
创建型模式由两个主导思想构成。一是将系统使用的具体类封装起来,二是隐藏这些具体类的实例创建和结合的方式。

实际上创建型模式的最大作用就是把对象的创建过程和使用过程进行了解耦,对使用者只提供接口而隐藏了具体的实现细节,让软件结构更清晰,更符合单一职责的原则。接下来本文将继续介绍创建型模式中的另两种模式——建造者模式和原型模式,文中的代码已托管在Github上。

建造者模式

建造者模式(Builder Pattern)与工厂模式类似,也是把产品的实例化过程交给专门的类来实现,不同的是建造者模式更多的是针对现实中一些构成较复杂,有多个组成部分的对象。比如像汽车就由车身、发动机、车轮、方向盘等很多部件组成,而且整个组装的过程可能还要安装一定的顺序进行。

class Car(object):
    """产品
    """
    def __init__(self, name):
        self.name = name
        self.body = None
        self.engine = None
        self.tire = None

    def __str__(self):
        info = ("Name: {}".format(self.name),
                "Body: {}".format(self.body),
                "Engine: {}".format(self.engine),
                "Tire: {}".format(self.tire))
        return '\n'.join(info)

但是在现实使用时,用户可能并不关心汽车的这些细节和如何将这些部件组装成汽车的,而只是想通过特定的接口和参数获得汽车这个对象。这时就需要将这个复杂的过程抽象到到一个被称作建造者的对象里,建造者来负责构造这些复杂的组成:

class CarBuilder(object):
    """建造者
    """
    def __init__(self):
        self.car = Car("Mercedes")

    def add_body(self, body):
        self.car.body = body

    def add_engine(self, engine):
        """AMG 5.5L V8 biturbo"""
        self.car.engine = engine

    def add_tire(self, tire):
        self.car.tire = tire

    def assemble_car(self):
        return self.car

最后建造者模式中还会引入了一个指挥者类的角色,该类的作用主要是负责精确地控制产品的整个生成过程,根据用户的不同需求返回不同的完整产品对象。

class Engineer(object):
    """指挥者
    """
    def __init__(self):
        self.builder = None

    def construct_car(self, body, engine, tire):
        self.builder = CarBuilder()
        self.builder.add_body(body)
        self.builder.add_engine(engine)
        self.builder.add_tire(tire)
        return self.builder.assemble_car()

只要把需求告诉指挥者,用户就可以获得一个产品的实例,如下所示:

engineer = Engineer()
car = engineer.construct_car(
        body="G63",
        engine="AMG 5.5L V8 biturbo",
        tire="Michelin 18inch")
print(car)

# Output
>Name: Mercedes
>Body: G63
>Engine: AMG 5.5L V8 biturbo
>Tire: Michelin 18inch

原型模式

原型模式(Prototype design pattern)的动机是为了让用户可以通过复制对象获得一个对象的副本。此模式的出现是因为在C++和Java里需要用此模式来更方便的拷贝对象,但在Python中要实现对一个对象的拷贝有更简单办法,我们就在这里简单的举个例子:

#coding=utf-8
 class Foo(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return "id: {}, x: {}, y: {}".format(id(self), self.x, self.y)

if __name__ == '__main__':
    foo = Foo(1, 2)
    # 利用deepcopy获得新对象
    import copy
    foo1 = copy.deepcopy(foo)
    foo1.x = 3
    foo1.y = 4
    print(foo, foo1)

    # 利用__class__方法获得新
    foo2 = foo1.__class__(5, 6)
    print(foo, foo2)

# Output
>id: 4312696592, x: 1, y: 2 id: 4312696928, x: 3, y: 4
>id: 4312696592, x: 1, y: 2 id: 4312697096, x: 5, y: 6

上面我们用两种原型方式实现了对Foo对象的拷贝,第一种是利用Python语言内置的deepcopy,第二种则用了更优雅的__class__方法。

结论

本文介绍的两种模式在实际使用中并不常见,建造者模式应用在构造一些复杂的对象时,比如需要构建一个HTML对象,窗口对象或者游戏中的建模等等,而原型模式则主要应用于一些需要备份状态的对象或新建对象开销过大的时候。

参考

[1]维基百科

Was mich nicht umbringt, macht mich stärker.

33 声望
3 粉丝
0 条评论
推荐阅读
15分钟入门23种设计模式:图解,范例和对比
本文力图在15分钟内,通过UML图解、范例和类比,让你对面向对象的23种设计模式形成提纲挈领的认识,从而让我们在面临代码设计问题时更加成竹在胸。本文源代码: UML, Sample Code。

风云信步5阅读 434评论 1

python里打印list的四种方法
原文链接标题:Print lists in Python (4 Different Ways)用for循环来打印 {代码...} 结果1 2 3 4 5用 * 星号来打印 {代码...} 结果 {代码...} 把list转换为str来打印 {代码...} 结果 {代码...} 用map把数组里非...

chiiinnn阅读 10.3k

封面图
工作中常用的设计模式--享元模式
一般做业务开发,不太容易有大量使用设计模式的场景。这里总结一下在业务开发中使用较为频繁的设计模式。当然语言为Java,基于Spring框架。

lpe2344阅读 972

封面图
Ubuntu20.04 从源代码编译安装 python3.10
Ubuntu 22.04 Release DateUbuntu 22.04 Jammy Jellyfish is scheduled for release on April 21, 2022If you’re ready to use Ubuntu 22.04 Jammy Jellyfish, you can either upgrade your current Ubuntu syste...

ponponon1阅读 4k评论 1

日常Python 代码片段整理
1、简单的 HTTP Web 服务器 {代码...} 2、单行循环List {代码...} 3、更新字典 {代码...} 4、拆分多行字符串 {代码...} 5、跟踪列表中元素的频率 {代码...} 6、不使用 Pandas 读取 CSV 文件 {代码...} 7、将列表...

墨城2阅读 308

Unicode 正则表达式(qbit)
前言本文根据《精通正则表达式》和 Unicode Regular Expressions 整理。本文的示例默认以 Python3 为实现语言,用到 Python3 的 re 模块或 regex 库。基本的 Unicode 属性分类 {代码...} 基本的 Unicode 子属性Le...

qbit阅读 4.4k

Python + Sqlalchemy 对数据库的批量插入或更新(Upsert)
由于不同数据库对这种 upsert 的实现机制不同,Sqlalchemy 也就不再试图做一致性的封装了,而是提供了各自的方言 API,具体到 Mysql,就是给 insert statement ,增加了 on_duplicate_key_update 方法。

songofhawk1阅读 2k评论 4

封面图

Was mich nicht umbringt, macht mich stärker.

33 声望
3 粉丝
宣传栏