Python学习笔记(5)Python的创建型设计模式

news2024/12/27 10:19:35

创建型设计模式(Creational Design Patterns),主要关注对象的创建机制。这类模式可以使得系统更加独立于如何创建、组合和表示其对象。通过将这些职责分离出来,创建型设计模式有助于提高代码的灵活性和复用性。

本书的范例代码已经放在我的资源库里——pipbook

1 抽象工厂模式

用于创建复杂的对象,这种对象由许多小对象组成,而这些对象都属于某个特定的系列

例如,在GUI系统中可以设计一个“抽象控件工厂”,然后在下面设计三个“具体子类工厂”,如下图所示:
在这里插入图片描述

它们都提供同一种对象的方法(例如都提供创建按钮的方法等),但是可以创建适应不同的平台的方法。

抽象工厂模式的结构:

  1. 抽象工厂(Abstract Factory):定义了创建一系列相关产品对象的方法(例如,createProductA()、createProductB()等)。
  2. 具体工厂(Concrete Factory):实现了抽象工厂中定义的方法,负责实例化具体的产品对象。
  3. 抽象产品(Abstract Product):定义了产品的接口,可以是一个产品家族的抽象基类。
  4. 具体产品(Concrete Product):实现了抽象产品的接口,代表某一具体产品的实现。

假设有一个图形应用,支持创建不同风格的按钮和文本框。不同风格的按钮和文本框属于不同的产品族,例如,Windows风格和Mac风格,代码结构如下:

# 抽象工厂,创建按钮和文本框
class GUIFactory:
    def create_button(self):
        pass
    
    def create_textbox(self):
        pass

# 具体工厂,创建win或者mac的按钮和文本框
class WindowsFactory(GUIFactory):
    def create_button(self):
        return WindowsButton()
    
    def create_textbox(self):
        return WindowsTextBox()

class MacFactory(GUIFactory):
    def create_button(self):
        return MacButton()
    
    def create_textbox(self):
        return MacTextBox()

# 抽象产品,定义按钮和文本框
class Button:
    def render(self):
        pass

class TextBox:
    def render(self):
        pass

# 具体产品,定义按钮和文本框的风格类型
class WindowsButton(Button):
    def render(self): # 重写render()
        print("Rendering Windows button")

class MacButton(Button):
    def render(self):# 重写render()
        print("Rendering Mac button")

class WindowsTextBox(TextBox):
    def render(self):# 重写render()
        print("Rendering Windows text box")

class MacTextBox(TextBox):
    def render(self):# 重写render()
        print("Rendering Mac text box")


# 客户端,factory 必须是 GUIFactory 类型或其子类
def client_code(factory: GUIFactory):
    '''
    功能:创建对象

    如果传入WindowsFactory(),则实际上调用:
    
    def create_button(self):
        return WindowsButton()
    
    def create_textbox(self):
        return WindowsTextBox()
    '''
    button = factory.create_button()
    textbox = factory.create_textbox()

    # 使用对象
    button.render()
    textbox.render()

# 使用不同的工厂
windows_factory = WindowsFactory()
client_code(windows_factory)

mac_factory = MacFactory()
client_code(mac_factory)

优点

  • 解耦:客户端不需要依赖具体的产品类,只需要依赖抽象工厂接口,可以轻松切换不同的产品族。
  • 可扩展性:可以轻松添加新的产品族,只需增加新的具体工厂和产品类(抽象产品),而不需要修改现有的代码

1.1 经典的抽象工厂模式

我们通过生成简单的“示意图”来进行演示,这段程序定义了两个工厂:一个生成纯文本格式的示意图,一个生成SVG格式的示意图。
在这里插入图片描述

同时此程序采用了两种写法:

  • diagram1.py按照传统方式来运用抽象工厂模式
  • diagram2.py借助了python的特性,使得写出来的程序更短小清晰

下面是diagram1.py的代码结构:
在这里插入图片描述
代码主要结构的解释如下:

  1. 有一个create_diagram()的抽象工厂,输入参数是具体工厂
  2. 有两个具体工厂类,既是基类,也定义了抽象接口(创建图、创建矩形、创建文本)
  3. 有三个抽象产品,DiagramRectangleText,定义了图形、矩阵、文本的具体行为
  4. 有六个具体产品:
    Diagram(ASCII 格式图形)
    SvgDiagram(SVG 格式图形)
    Rectangle(ASCII 格式矩形)
    SvgRectangle(SVG 格式矩形)
    Text(ASCII 格式文本)
    SvgText(SVG 格式文本)
    具体产品是抽象产品的实现类,是工厂具体生产出来的产品,不过由于设置了限制,不同系列的产品不能进行混搭。

这样,如果想扩展的具体的产品,可以直接增加具体产品就行;如果想增加新的组建,则增加新的抽象产品和具体产品,而不用再去修改抽象工厂和具体工厂。

1.2 Python风格的抽象工厂模式

上面的写法演示了传统的抽象工厂模式,但是也有几个缺点:

  1. SvgDiagramFactoryDiagramFactory的代码几乎一样,有很多重复代码
  2. 两个具体工厂都没有需要初始化的变量,所以根本不需要创建实例
  3. 六个具体产品都放在了“顶级命名空间”中,所以为了导致名称冲突,只得加上前缀,但实际上可以不这样做

下面是diagram1.py的代码结构,使用了一些Python的特性,让代码变得更简洁:
在这里插入图片描述

通过使用@classmethod装饰器定义类方法,可以来解决这些问题:
在这里插入图片描述
如上图所示,我们把创建图、矩形、文本的方法都嵌套到DiagramFactory中,并定义为类方法,这样:

  1. SvgDiagramFactory只需要继承DiagramFactory即可,不需要再去实现那三个方法,提升代码简洁性。
  2. 由于使用了类方法,可以直接通过DiagramFactory.make_diagram()来调用,不需要再创建一个实例。
  3. SvgDiagramFactoryDiagramFactory中,我们都可以使用DiagramTextRectangle去定义类,因为他们分别属于SvgDiagramFactoryDiagramFactory下面,所以不需要再添加前缀,此时“顶级命名空间”中只剩下了create_Ddagram()DiagramFactorySvgDiagramFactory

2 建造者模式

也叫生成器模式、构建者模式,与抽象工厂类似,都可以创建由其他对象组合而成的复杂对象,但建造者模式还会保存复杂对象里各个部分的细节。

建造者模式分步骤构造复杂的对象。这种模式的主要目的是将一个复杂对象的构建与其表示分离,使得相同的构建过程可以创建不同的表示。建造者模式通常用于需要根据不同的参数组合来创建不同配置的对象场景。

建造者模式的结构:

  1. 抽象建造者: 定义了一个接口,规定了所有具体建造者必须实现的方法。
  2. 具体建造者: 继承自 AbstractFormBuilder,并提供了具体的实现,每个具体建造者负责构建特定类型的产品
  3. 导演类: 负责指导构建过程。它接收一个建造者实例,并调用建造者的各个方法以逐步构建最终产品,它不关心具体是如何构建产品的,只负责按照一定的规则或流程调用建造者的方法。

这里通过“表单生成程序“来演示,这段程序可以生成一个HTML的表单和一个Tkinter的GUI表单,代码文件是formbuilder.py:
在这里插入图片描述

  1. 有一个抽象建造者AbstractFormBuilder,定义了如 add_title(), form(), add_label(), add_entry(), 和 add_button() 方法,这些方法定义了构建过程中的步骤。
  2. 有两个具体建造HtmlFormBuilderTkFormBuilder,分别是继承自AbstractFormBuilder,并且根据各自的场景,对方法进行了重写。
  3. 有一个导演类create_login_form,指出了构造流程,如下:在这里插入图片描述
    builder参数是指HtmlFormBuilder()或者TkFormBuilder()main()函数也只负责“指挥”导演类进行搭建

优点:

  • 灵活性与可扩展性: 建造者模式使得代码更加灵活和易于扩展。如果需要添加新的表单类型,只需要创建一个新的具体建造者类,然后在main()中选择使用它即可。
  • 控制复杂性: 它隐藏了对象创建的具体细节,使main()代码更加简洁,可以更改产品的内部结构而不影响其他代码

3 工厂方法模式

这个模式的核心思想是定义一个用于创建对象的接口,但由子类决定实例化哪一个类。这样,工厂方法让类的实例化推迟到子类。这种模式的主要优点是它遵循了开闭原则(Open/Closed Principle),即软件实体对扩展开放,对修改关闭。通过使用工厂方法模式,你可以在不修改现有代码的情况下引入新的产品类型。

工厂方法模式的结构:

  1. Product(产品接口): 定义所有具体产品类的公共接口。客户端代码使用这个接口来操作具体的对象,而无需关心具体的实现细节。
  2. Concrete Product(具体产品): 实现了Product接口的具体类。每个具体产品代表一个可以被创建的对象。
  3. Creator(创建者/抽象工厂): 包含了一个或多个工厂方法,用于声明创建Product对象的接口。它通常是一个抽象类,定义了工厂方法但没有实现它
  4. Concrete Creator(具体创建者/具体工厂): 继承自Creator,并实现了工厂方法以返回一个特定类型的Concrete Product实例。

gameboard4.py为例:

  1. 产品接口是Piece

  2. 具体产品是通过 exec 动态创建的类,继承自 Piece 类。如下图代码:
    在这里插入图片描述

  3. LAbstractBoard 类是创建者,它定义了 populate_board() 抽象方法,子类需要实现此方法来创建具体的棋子

  4. heckersBoardChessBoard 类分别是具体创建者,它们继承自 AbstractBoard,并且实现了 populate_board() 方法来具体创建棋子并将其放置到棋盘上。

优点:

  • 提高代码可扩展性: 例如,在这段代码中,若想增加新的棋子类型(如“象棋”中的“炮”棋子),只需要新增一个新的棋子类和相应的创建方法,而不需要修改棋盘类的其他部分。
  • 提供了灵活的产品替换机制: 比如,如果要从国际象棋变更为跳棋,只需要在 CheckersBoard 类中调整 populate_board 方法,而不需要修改整个代码结构。
  • 符合开放封闭原则: 例如,假如我们要添加一个新的棋类游戏(如围棋),我们只需要新建一个围棋棋盘类,并实现相关的工厂方法来生成围棋棋子,而现有的国际象棋和跳棋代码不会受到影响。

4 原型模式

原型模式通过复制现有的对象来创建新对象,而不是通过构造函数来直接创建新对象。它的核心思想是使用“原型”对象作为模板,复制它来创建新的对象,这种方式可以提高性能,尤其是在创建复杂对象时,避免了重复的初始化工作。

关键概念:

  • 原型(Prototype): 一个可以被复制的对象,它包含了创建其他相似对象的能力。
  • 克隆(Clone): 通过复制原型对象来创建新的对象。通常,原型类提供一个 clone() 方法来完成这个任务。
  • 浅拷贝与深拷贝:
    浅拷贝: 仅复制对象的基本类型属性,复杂类型的属性(如列表、对象等)仍然指向原对象中的同一引用。
    深拷贝: 复制对象及其所有子对象,完全独立于原对象。

原型模式的结构:

  1. Prototype(原型): 声明一个 clone() 方法,用于复制当前对象。
  2. ConcretePrototype(具体原型): 实现 clone() 方法,复制当前对象的所有属性并返回一个新的对象实例。
  3. Client(客户端): 使用原型对象,通过调用 clone() 方法来获得新的对象。

示例代码:

import copy

# 1. 原型类,提供 clone 方法
class Prototype:
    def clone(self):
        # 默认浅拷贝
        return copy.copy(self)

# 2. 具体原型类
class ConcretePrototype(Prototype):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return f"{self.name}, {self.age}"

    def clone(self):
        # 这里进行深拷贝
        return copy.deepcopy(self)

# 3. 客户端代码
def main():
    original = ConcretePrototype("Alice", 30)
    print("Original:", original)

    # 使用原型模式创建副本
    clone1 = original.clone()
    print("Clone1:", clone1)

    # 修改副本的属性,确保原型不受影响
    clone1.name = "Bob"
    clone1.age = 25
    print("Modified Clone1:", clone1)
    print("Original after modifying Clone1:", original)

if __name__ == "__main__":
    main()

  • Prototype 类是一个抽象类,定义了 clone() 方法,具体的原型类需要实现这个方法来复制对象。
  • ConcretePrototype 中,clone() 方法使用了 copy.deepcopy() 来实现深拷贝,这样我们得到的副本对象与原始对象完全独立,不会相互影响。

优点:

  1. 如果某些对象的创建过程比较复杂,且多个地方需要相似的对象,可以通过原型模式来减少重复工作。
  2. 如果需要在运行时创建大量相似的对象,可以通过原型模式来提高性能。
  3. 如果产品有多种不同变体,但这些变体之间有相似的结构,可以通过原型模式来共享公共部分。

5 单例模式

单例模式确保一个类只有一个实例,并提供全局访问点来获取这个实例。该模式主要用于限制类的实例化次数,保证系统中某个类始终只有一个实例,适用于那些只需要一个共享资源或全局配置的场景。

关键特性:

  1. 唯一性: 确保类只有一个实例。
  2. 全局访问点: 提供全局访问该实例的方式。
  3. 懒加载: 实例仅在第一次使用时创建,而不是一开始就创建。

6 总结

介绍了5种创建型设计模式,其中:

  • 单例模式可以用Python的模块来实现,没有特别的地方。
  • 由于Python可以动态的访问类对象,所以原型模式也什么意义(python提供了内置的copy方法)
  • 所以最有用的是抽象工厂模式工厂方法模式建造者模式

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2251684.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

云备份实战项目

文章目录 前言一、整体项目简介二、服务端环境及功能简介三、 客户端环境及功能简介四、服务端文件管理类的实现1. 获取文件大小,最后一次修改时间,最后一次访问时间,文件名称,以及文件内容的读写等功能2. 判断文件是否存在&#…

Java有关数组的相关问题

Java中的栈和堆的含义 栈 存储局部变量:栈主要用于存储方法中的局部变量,包括基本数据类型(int、double、boolean等)和对象的引用(不包含对象本身)。 遵循后进先出原则:当一个方法被调用时&…

使用Dify与BGE-M3搭建RAG(检索增强生成)应用-改进一,使用工作流代替Agnet

文章目录 前言Agent vs 工作流编写工作流 前言 在上一篇中,我们实现了一个基本的基于Dify的RAG的示范。 使用Dify与BGE-M3搭建RAG(检索增强生成)应用 这个效果确实很差。 我们一起来看看,该怎么改进。 今天我们就尝试一下&…

Python语法基础(四)

🌈个人主页:羽晨同学 💫个人格言:“成为自己未来的主人~” 高阶函数之map 高阶函数就是说,A函数作为B函数的参数,B函数就是高阶函数 map:映射 map(func,iterable) 这个是map的基本语法,…

Python毕业设计选题:基于django+vue的智慧社区可视化平台的设计与实现+spider

开发语言:Python框架:djangoPython版本:python3.7.7数据库:mysql 5.7数据库工具:Navicat11开发软件:PyCharm 系统展示 管理员登录 管理员功能界面 养老机构管理 业主管理 社区安防管理 社区设施管理 车位…

Ubuntu环境中RocketMQ安装教程

参考教程 https://blog.csdn.net/weixin_56219549/article/details/126143231 1、安装JDK,并配置环境变量(略) 2、下载RocketMQ安装包 RocketMQ下载地址,选择二进制包下载 unzip rocketmq-all-5.0.0-ALPHA-bin-release.zip 使…

【SSM】mybatis的增删改查

目录 代理Dao方式的增删改查 1. 创建项目 $$1. 在sql.xml里增加日志代码以及user的mapper资源。 $$ 2. 在usermapper里引入接口。 $$3. 在测试类中引入以下代码,并修改其中名字。 $$ 4. 实例对象User.java里属性要与表中列严格对应。 2. 查询 1>. 查询所有 …

【C++习题】23.二分查找算法_寻找旋转排序数组中的最小值

文章目录 题目链接:题目描述:解法C 算法代码:图解 题目链接: 153. 寻找旋转排序数组中的最小值 题目描述: 解法 暴力解法:O(n) 从前往后找数组中的最小值。 二分算法:O(logn) 先找二段性 因为这…

Linux笔记---进程:进程终止

1. 进程终止概念与分类 进程终止是指一个正在运行的进程结束其执行的操作。以下是一些常见的导致进程终止的情况: 一、正常终止 完成任务当进程完成了它被设计要执行的任务后,就会正常终止。收到特定信号在操作系统中,进程可能会收到来自操作…

立创庐山派 K230 RTSP 推流

立创庐山派使用的是K230芯片,按照教程刷了canmv固件,下载canmv ide,使用嘉楠社区的rtsp和wlan例程,修改成连接wifi以及RTSP推流例程 # Description: This example demonstrates how to stream video and audio to the network us…

Oracle数据恢复—Oracle数据库sysaux文件损坏的数据恢复案例

Oracle数据库故障&分析: 一台Oracle数据库打开报错,报错信息: “system01.dbf需要更多的恢复来保持一致性,数据库无法打开”。管理员联系我们数据恢复中心寻求帮助,并提供了Oracle_Home目录的所有文件。用户方要求…

【kafka04】消息队列与微服务之Kafka 图形工具

Kafka 在 ZooKeeper 里面的存储结构 topic 结构 /brokers/topics/[topic] partition结构 /brokers/topics/[topic]/partitions/[partitionId]/state broker信息 /brokers/ids/[o...N] 控制器 /controller 存储center controller中央控制器所在kafka broker的信息 消费者 /c…

微众银行前端面试题及参考答案

使用的协议是 HTTP 还是 HTTPS,为什么没用 HTTPS? 在前端开发中,有些网站使用 HTTP 协议,有些使用 HTTPS 协议。 使用 HTTP 协议的情况可能是因为网站对安全性的要求不是极高,或者处于开发的早期阶段,还没有…

第144场双周赛:移除石头游戏、两个字符串得切换距离、零数组变换 Ⅲ、最多可收集的水果数目

Q1、[简单] 移除石头游戏 1、题目描述 Alice 和 Bob 在玩一个游戏,他们俩轮流从一堆石头中移除石头,Alice 先进行操作。 Alice 在第一次操作中移除 恰好 10 个石头。接下来的每次操作中,每位玩家移除的石头数 恰好 为另一位玩家上一次操作…

UR开始打中国牌,重磅发布国产化协作机器人UR7e 和 UR12e

近日,优傲(UR)机器人公司立足中国市场需求,重磅推出UR7e和UR12e 两款本地化协作机器人。它们延续优傲(UR)一以贯之的高品质与性能特质,着重优化负载自重比,且在价格层面具竞争力&…

应急响应靶机——Windows挖矿事件

载入虚拟机,开启虚拟机: (账户密码:administrator/zgsf123) 发现登录进去就弹出终端界面,自动运行powshell命令,看来存在计划任务,自动下载了一些文件,之后就主动结束退…

基于深度学习和卷积神经网络的乳腺癌影像自动化诊断系统(PyQt5界面+数据集+训练代码)

乳腺癌是全球女性中最常见的恶性肿瘤之一,早期准确诊断对于提高生存率具有至关重要的意义。传统的乳腺癌诊断方法依赖于放射科医生的经验,然而,由于影像分析的复杂性和人类判断的局限性,准确率和一致性仍存在挑战。近年来&#xf…

深入浅出机器学习中的梯度下降算法

大家好,在机器学习中,梯度下降算法(Gradient Descent)是一个重要的概念。它是一种优化算法,用于最小化目标函数,通常是损失函数。梯度下降可以帮助找到一个模型最优的参数,使得模型的预测更加准…

PotPlayer 最新版本支持使用 Whisper 自动识别语音生成字幕

PotPlayer 最新版本支持使用 Whisper 自动识别语音生成字幕 设置使用下载地址 设置 使用 下载地址 https://www.videohelp.com/software/PotPlayer

【0x0001】HCI_Set_Event_Mask详解

目录 一、命令概述 二、命令格式 三、命令参数说明 四、返回参数说明 五、命令执行流程 5.1. 主机准备阶段 5.2. 命令发送阶段 5.3. 控制器接收与处理阶段 5.4. 事件过滤与反馈阶段 5.5. 主机处理(主机端) 5.6. 示例代码 六、命令应用场景 …