Python学习笔记(5)Python的创建型设计模式
创建型设计模式(Creational Design Patterns),主要关注对象的创建机制。这类模式可以使得系统更加独立于如何创建、组合和表示其对象。通过将这些职责分离出来,创建型设计模式有助于提高代码的灵活性和复用性。
本书的范例代码已经放在我的资源库里——pipbook
1 抽象工厂模式
用于创建复杂的对象,这种对象由许多小对象组成,而这些对象都属于某个特定的系列
例如,在GUI系统中可以设计一个“抽象控件工厂”,然后在下面设计三个“具体子类工厂”,如下图所示:

它们都提供同一种对象的方法(例如都提供创建按钮的方法等),但是可以创建适应不同的平台的方法。
抽象工厂模式的结构:
- 抽象工厂(Abstract Factory):定义了创建一系列相关产品对象的方法(例如,createProductA()、createProductB()等)。
- 具体工厂(Concrete Factory):实现了抽象工厂中定义的方法,负责实例化具体的产品对象。
- 抽象产品(Abstract Product):定义了产品的接口,可以是一个产品家族的抽象基类。
- 具体产品(Concrete Product):实现了抽象产品的接口,代表某一具体产品的实现。
假设有一个图形应用,支持创建不同风格的按钮和文本框。不同风格的按钮和文本框属于不同的产品族,例如,Windows风格和Mac风格,代码结构如下:
# 抽象工厂,创建按钮和文本框
class GUIFactory:def create_button(self):passdef 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):passclass 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的代码结构:

代码主要结构的解释如下:
- 有一个
create_diagram()的抽象工厂,输入参数是具体工厂 - 有两个具体工厂类,既是基类,也定义了抽象接口(创建图、创建矩形、创建文本)
- 有三个抽象产品,
Diagram、Rectangle、Text,定义了图形、矩阵、文本的具体行为 - 有六个具体产品:
Diagram(ASCII 格式图形)
SvgDiagram(SVG 格式图形)
Rectangle(ASCII 格式矩形)
SvgRectangle(SVG 格式矩形)
Text(ASCII 格式文本)
SvgText(SVG 格式文本)
具体产品是抽象产品的实现类,是工厂具体生产出来的产品,不过由于设置了限制,不同系列的产品不能进行混搭。
这样,如果想扩展的具体的产品,可以直接增加具体产品就行;如果想增加新的组建,则增加新的抽象产品和具体产品,而不用再去修改抽象工厂和具体工厂。
1.2 Python风格的抽象工厂模式
上面的写法演示了传统的抽象工厂模式,但是也有几个缺点:
SvgDiagramFactory和DiagramFactory的代码几乎一样,有很多重复代码- 两个具体工厂都没有需要初始化的变量,所以根本不需要创建实例
- 六个具体产品都放在了“顶级命名空间”中,所以为了导致名称冲突,只得加上前缀,但实际上可以不这样做
下面是diagram1.py的代码结构,使用了一些Python的特性,让代码变得更简洁:

通过使用@classmethod装饰器定义类方法,可以来解决这些问题:

如上图所示,我们把创建图、矩形、文本的方法都嵌套到DiagramFactory中,并定义为类方法,这样:
SvgDiagramFactory只需要继承DiagramFactory即可,不需要再去实现那三个方法,提升代码简洁性。- 由于使用了类方法,可以直接通过
DiagramFactory.make_diagram()来调用,不需要再创建一个实例。 - 在
SvgDiagramFactory和DiagramFactory中,我们都可以使用Diagram、Text、Rectangle去定义类,因为他们分别属于SvgDiagramFactory和DiagramFactory下面,所以不需要再添加前缀,此时“顶级命名空间”中只剩下了create_Ddagram()、DiagramFactory、SvgDiagramFactory
2 建造者模式
也叫生成器模式、构建者模式,与抽象工厂类似,都可以创建由其他对象组合而成的复杂对象,但建造者模式还会保存复杂对象里各个部分的细节。
建造者模式分步骤构造复杂的对象。这种模式的主要目的是将一个复杂对象的构建与其表示分离,使得相同的构建过程可以创建不同的表示。建造者模式通常用于需要根据不同的参数组合来创建不同配置的对象场景。
建造者模式的结构:
- 抽象建造者: 定义了一个接口,规定了所有具体建造者必须实现的方法。
- 具体建造者: 继承自
AbstractFormBuilder,并提供了具体的实现,每个具体建造者负责构建特定类型的产品 - 导演类: 负责指导构建过程。它接收一个建造者实例,并调用建造者的各个方法以逐步构建最终产品,它不关心具体是如何构建产品的,只负责按照一定的规则或流程调用建造者的方法。
这里通过“表单生成程序“来演示,这段程序可以生成一个HTML的表单和一个Tkinter的GUI表单,代码文件是formbuilder.py:

- 有一个抽象建造者
AbstractFormBuilder,定义了如add_title(), form(), add_label(), add_entry(), 和 add_button()方法,这些方法定义了构建过程中的步骤。 - 有两个具体建造
HtmlFormBuilder和TkFormBuilder,分别是继承自AbstractFormBuilder,并且根据各自的场景,对方法进行了重写。 - 有一个导演类
create_login_form,指出了构造流程,如下:
builder参数是指HtmlFormBuilder()或者TkFormBuilder(),main()函数也只负责“指挥”导演类进行搭建
优点:
- 灵活性与可扩展性: 建造者模式使得代码更加灵活和易于扩展。如果需要添加新的表单类型,只需要创建一个新的具体建造者类,然后在
main()中选择使用它即可。 - 控制复杂性: 它隐藏了对象创建的具体细节,使
main()代码更加简洁,可以更改产品的内部结构而不影响其他代码
3 工厂方法模式
这个模式的核心思想是定义一个用于创建对象的接口,但由子类决定实例化哪一个类。这样,工厂方法让类的实例化推迟到子类。这种模式的主要优点是它遵循了开闭原则(Open/Closed Principle),即软件实体对扩展开放,对修改关闭。通过使用工厂方法模式,你可以在不修改现有代码的情况下引入新的产品类型。
工厂方法模式的结构:
- Product(产品接口): 定义所有具体产品类的公共接口。客户端代码使用这个接口来操作具体的对象,而无需关心具体的实现细节。
- Concrete Product(具体产品): 实现了Product接口的具体类。每个具体产品代表一个可以被创建的对象。
- Creator(创建者/抽象工厂): 包含了一个或多个工厂方法,用于声明创建Product对象的接口。它通常是一个抽象类,定义了工厂方法但没有实现它
- Concrete Creator(具体创建者/具体工厂): 继承自Creator,并实现了工厂方法以返回一个特定类型的Concrete Product实例。
以gameboard4.py为例:
-
产品接口是
Piece类 -
具体产品是通过
exec动态创建的类,继承自Piece类。如下图代码:

-
LAbstractBoard类是创建者,它定义了populate_board()抽象方法,子类需要实现此方法来创建具体的棋子。 -
heckersBoard和ChessBoard类分别是具体创建者,它们继承自AbstractBoard,并且实现了populate_board()方法来具体创建棋子并将其放置到棋盘上。
优点:
- 提高代码可扩展性: 例如,在这段代码中,若想增加新的棋子类型(如“象棋”中的“炮”棋子),只需要新增一个新的棋子类和相应的创建方法,而不需要修改棋盘类的其他部分。
- 提供了灵活的产品替换机制: 比如,如果要从国际象棋变更为跳棋,只需要在 CheckersBoard 类中调整 populate_board 方法,而不需要修改整个代码结构。
- 符合开放封闭原则: 例如,假如我们要添加一个新的棋类游戏(如围棋),我们只需要新建一个围棋棋盘类,并实现相关的工厂方法来生成围棋棋子,而现有的国际象棋和跳棋代码不会受到影响。
4 原型模式
原型模式通过复制现有的对象来创建新对象,而不是通过构造函数来直接创建新对象。它的核心思想是使用“原型”对象作为模板,复制它来创建新的对象,这种方式可以提高性能,尤其是在创建复杂对象时,避免了重复的初始化工作。
关键概念:
- 原型(Prototype): 一个可以被复制的对象,它包含了创建其他相似对象的能力。
- 克隆(Clone): 通过复制原型对象来创建新的对象。通常,原型类提供一个 clone() 方法来完成这个任务。
- 浅拷贝与深拷贝:
浅拷贝: 仅复制对象的基本类型属性,复杂类型的属性(如列表、对象等)仍然指向原对象中的同一引用。
深拷贝: 复制对象及其所有子对象,完全独立于原对象。
原型模式的结构:
- Prototype(原型): 声明一个 clone() 方法,用于复制当前对象。
- ConcretePrototype(具体原型): 实现 clone() 方法,复制当前对象的所有属性并返回一个新的对象实例。
- 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 = nameself.age = agedef __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 = 25print("Modified Clone1:", clone1)print("Original after modifying Clone1:", original)if __name__ == "__main__":main()
Prototype类是一个抽象类,定义了clone()方法,具体的原型类需要实现这个方法来复制对象。- 在
ConcretePrototype中,clone()方法使用了copy.deepcopy()来实现深拷贝,这样我们得到的副本对象与原始对象完全独立,不会相互影响。
优点:
- 如果某些对象的创建过程比较复杂,且多个地方需要相似的对象,可以通过原型模式来减少重复工作。
- 如果需要在运行时创建大量相似的对象,可以通过原型模式来提高性能。
- 如果产品有多种不同变体,但这些变体之间有相似的结构,可以通过原型模式来共享公共部分。
5 单例模式
单例模式确保一个类只有一个实例,并提供全局访问点来获取这个实例。该模式主要用于限制类的实例化次数,保证系统中某个类始终只有一个实例,适用于那些只需要一个共享资源或全局配置的场景。
关键特性:
- 唯一性: 确保类只有一个实例。
- 全局访问点: 提供全局访问该实例的方式。
- 懒加载: 实例仅在第一次使用时创建,而不是一开始就创建。
6 总结
介绍了5种创建型设计模式,其中:
- 单例模式可以用Python的模块来实现,没有特别的地方。
- 由于Python可以动态的访问类对象,所以原型模式也什么意义(python提供了内置的copy方法)
- 所以最有用的是抽象工厂模式、工厂方法模式、建造者模式
相关文章:
Python学习笔记(5)Python的创建型设计模式
创建型设计模式(Creational Design Patterns),主要关注对象的创建机制。这类模式可以使得系统更加独立于如何创建、组合和表示其对象。通过将这些职责分离出来,创建型设计模式有助于提高代码的灵活性和复用性。 本书的范例代码已经…...
qt QAnimationDriver详解
1、概述 QAnimationDriver是Qt框架中提供的一个类,它主要用于自定义动画帧的时间控制和更新。通过继承和实现QAnimationDriver,开发者可以精确控制动画的时间步长和更新逻辑,从而实现丰富和灵活的动画效果。QAnimationDriver与QAbstractAnim…...
零拷贝相关知识点(一)
前言 大家好,我是程序员田螺。 零拷贝是老生常谈的问题啦,大厂非常喜欢问。比如Kafka为什么快,RocketMQ为什么快等,都涉及到零拷贝知识点。最近技术讨论群几个伙伴分享了阿里、虾皮的面试真题,也都涉及到零拷贝。因此…...
STM32的CAN波特率计算
公式: CAN波特率 APB总线频率 / (BRP分频器 1)/ (SWJ BS1 BS2) SWJ一般为1。 例如STM32F407的,CAN1和CAN2都在在APB1下,频率是42000000 如果想配置成1M波特率,则计算公式为:...
简单好用的折线图绘制!
折线图的概念及作用: 折线图(Line Chart)是一种常见的图表类型,用于展示数据的变化趋势或时间序列数据。它通过一系列的数据点(通常表示为坐标系中的点)与这些点之间的线段相连,直观地展示变量…...
Hadoop批量计算实验
参考: Hadoop(一)之实验一CentOS7配置Hadoop系统:配置CentOS和下载安装包_基于虚拟机cents7搭建hadoop实验目的-CSDN博客 --------------------------------------------------------- 一、安装Vmware 二、创建虚拟机 1.安装centos7 ①打开VMware,点击新建虚拟机。 …...
基于rpcapd与wireshark的远程实时抓包的方法
基于rpcapd与wireshark的远程实时抓包的方法 服务端安装wireshark侧设置 嵌入式设备或服务器上没有图形界面,通常使用tcpdump抓包保存为pcap文件后,导出到本地使用wireshark打开分析,rpcapd可与wireshark配合提供一种远程实时抓包的方案&…...
ubuntu多版本安装gcc
1.ubuntu安装gcc 9.3.1 $ sudo apt update $ sudo apt install gcc-9 g-9 二、配置GCC版本 安装完成后,需要使用update-alternatives命令来配置GCC版本。这个命令允许系统在多个安装的版本之间进行选择 1.添加GCC 9.3.1到update-alternatives管理 $ sudo update-a…...
算法刷题Day1
BM47 寻找第k大 第一天就随便记录吧,万事开头难,我好不容易开的头,就别难为自己,去追求高质量了。嘿嘿嘿 题目 传送门 解题思路一:维护一个大小为k的最小堆。最后返回堆顶元素。 代码: # # 代码中的类名…...
泛化调用 :在没有接口的情况下进行RPC调用
什么是泛化调用? 在RPC调用的过程中,调用端向服务端发起请求,首先要通过动态代理,动态代理可以屏蔽RPC处理流程,使得发起远程调用就像调用本地一样。 RPC调用本质:调用端向服务端发送一条请求消息&#x…...
Java 泛型详细解析
泛型的定义 泛型类的定义 下面定义了一个泛型类 Pair,它有一个泛型参数 T。 public class Pair<T> {private T start;private T end; }实际使用的时候就可以给这个 T 指定任何实际的类型,比如下面所示,就指定了实际类型为 LocalDate…...
题解:CF332B Maximum Absurdity
CF332B CF332B 暴力思路 题目要我们找两个不重叠的区间,并使区间的值最大。那我们可以考虑使用双重循环搭配前缀和暴力求最大值。代码如下。 for(int i1;i<n;i) {ll lsum[ik-1]-sum[i-1],maxx;for(int jik;j<n;j){maxxlsum[jk-1]-sum[j-1];if(maxx>ans.…...
Vue 集成和使用 SQLite 的完整指东
1. 引言 SQLite 是一种轻量级的关系型数据库管理系统,以其简单易用、无需服务器等特点广泛应用于嵌入式系统、移动应用和小型应用程序中。在 Web 开发中,尤其是前端应用开发中,SQLite 可以作为客户端本地存储的一种选择,为用户提…...
【JVM什么时候触发YoungGC和FullGC】
YoungGC 年轻代Eden区满,就会触发YoungGC FullGC 老年代空间不足 经过多次GC后的大年龄对象会被放进老年代,或创建的大对象会直接在老年代分配,此时若老年代空间不足,就会触发FullGC。空间分配担保失败 触发YoungGC的时候会进行…...
ubuntu配置网络
1,设置桥接模式 1-1: 确定。 1-2: 编辑--->虚拟网络编辑器 刚安装ubuntu的时候,可能没有任何VMnet. 更改设置的目的: 添加VMnet0,并且设置VMnet为桥接模式--自动桥接。 如果没有VMnet0,选择添加网络…...
第十一课 Unity编辑器创建的资源优化_预制体和材质篇(Prefabs和Materials)详解
预制体(Prefabs) Unity中的预制体是用来存储游戏对象、子对象及其所需组件的可重用资源,一般来说预制体资源可充当资源模版,在此模版基础上可以在场景中创建新的预制体实例。 使用预制体的好处 由于预制体系统可以自动保持所有实例副本同步,…...
2024.11.29(单链表)
思维导图 声明文件 #ifndef __LINKLIST_H__ #define __LINKLIST_H__#include <myhead.h>typedef char datatype; //数据元素类型 //定义节点类型 typedef struct Node {union{int len; //头节点数据域datatype data; //普通节点数据域};struct Node *next; //指针域…...
基于深度学习和卷积神经网络的乳腺癌影像自动化诊断系统(PyQt5界面+数据集+训练代码)
乳腺癌是全球女性中最常见的恶性肿瘤之一,早期准确诊断对于提高生存率具有至关重要的意义。传统的乳腺癌诊断方法依赖于放射科医生的经验,然而,由于影像分析的复杂性和人类判断的局限性,准确率和一致性仍存在挑战。近年来…...
opengl 三角形
最后效果: OpenGL version: 4.1 Metal 不知道为啥必须使用VAO 才行。 #include <glad/glad.h> #include <GLFW/glfw3.h>#include <iostream> #include <vector>void framebuffer_size_callback(GLFWwindow *window, int width, int heigh…...
23种设计模式-抽象工厂(Abstract Factory)设计模式
文章目录 一.什么是抽象工厂设计模式?二.抽象工厂模式的特点三.抽象工厂模式的结构四.抽象工厂模式的优缺点五.抽象工厂模式的 C 实现六.抽象工厂模式的 Java 实现七.代码解析八.总结 类图: 抽象工厂设计模式类图 一.什么是抽象工厂设计模式?…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...
地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...
python打卡day49
知识点回顾: 通道注意力模块复习空间注意力模块CBAM的定义 作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...
【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...
【解密LSTM、GRU如何解决传统RNN梯度消失问题】
解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...
ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...
什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...
智能AI电话机器人系统的识别能力现状与发展水平
一、引言 随着人工智能技术的飞速发展,AI电话机器人系统已经从简单的自动应答工具演变为具备复杂交互能力的智能助手。这类系统结合了语音识别、自然语言处理、情感计算和机器学习等多项前沿技术,在客户服务、营销推广、信息查询等领域发挥着越来越重要…...
Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...
