设计模式之创建型模式

创建型模式与对象的创建有关。 创建型模式抽象了对象实例化的过程,这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。创建型模式有以下

工厂模式(Factory Method)

意图:定义一个用于创建对象的接口, 让子类决定实例化哪一个类
代码示例:

# 抽象产品类 - Shape
class Shape:def draw(self):pass# 具体产品类 - Circle
class Circle(Shape):def draw(self):print("Drawing a circle")# 具体产品类 - Rectangle
class Rectangle(Shape):def draw(self):print("Drawing a rectangle")# 工厂类 - ShapeFactory
class ShapeFactory:def create_shape(self, shape_type):if shape_type.lower() == "circle":return Circle()elif shape_type.lower() == "rectangle":return Rectangle()return None# 客户端代码
shape_factory = ShapeFactory()# 创建一个圆形对象
circle = shape_factory.create_shape("circle")
circle.draw()  # 输出:Drawing a circle# 创建一个矩形对象
rectangle = shape_factory.create_shape("rectangle")
rectangle.draw()  # 输出:Drawing a rectangle

在这个示例中,Shape是抽象产品类,CircleRectangle是具体产品类。ShapeFactory是工厂类,其中的create_shape方法根据传入的参数决定实例化哪个具体产品类来创建对象。客户端代码通过调用工厂类的方法来创建具体的产品对象,并调用其draw方法进行绘制。

抽象工厂模式(Abstract Factory)

意图:提供一个创建一系列相互依赖对象的接口,而无需指定他们具体的类
基于工厂模式的改良,工厂模式包括:抽象产品类、具体产品类、工厂类
抽象工厂模式的包括: 抽象产品类、具体产品类、抽象工厂类、具体工厂类

# 抽象工厂接口
class AbstractFactory:def create_engine(self):passdef create_tire(self):pass# 具体工厂实现类 - 奔驰工厂
class BenzFactory(AbstractFactory):def create_engine(self):return BenzEngine()def create_tire(self):return BenzTire()# 具体工厂实现类 - 宝马工厂
class BMWFactory(AbstractFactory):def create_engine(self):return BMWEngine()def create_tire(self):return BMWTire()# 抽象产品接口 - 引擎
class Engine:def description(self):pass# 具体产品类 - 奔驰引擎
class BenzEngine(Engine):def description(self):return "This is a Benz engine."# 具体产品类 - 宝马引擎
class BMWEngine(Engine):def description(self):return "This is a BMW engine."# 抽象产品接口 - 轮胎
class Tire:def description(self):pass# 具体产品类 - 奔驰轮胎
class BenzTire(Tire):def description(self):return "This is a Benz tire."# 具体产品类 - 宝马轮胎
class BMWTire(Tire):def description(self):return "This is a BMW tire."# 客户端代码
def client(factory):engine = factory.create_engine()tire = factory.create_tire()print(engine.description())print(tire.description())# 使用奔驰工厂创建产品
benz_factory = BenzFactory()
client(benz_factory)# 使用宝马工厂创建产品
bmw_factory = BMWFactory()
client(bmw_factory)

在上面的示例中,AbstractFactory 是抽象工厂接口,定义了创建产品的方法。BenzFactoryBMWFactory 是具体工厂实现类,分别负责创建奔驰和宝马汽车的引擎和轮胎。EngineTire 是抽象产品接口,定义了产品的方法。BenzEngineBMWEngineBenzTireBMWTire 是具体产品类,分别实现了奔驰和宝马汽车的引擎和轮胎。

客户端代码可以通过传入不同的具体工厂实例来创建不同品牌的汽车引擎和轮胎,并使用它们的方法。

抽象工厂方法模式可以方便地扩展产品系列,例如添加奥迪工厂和奥迪产品类,只需要新增对应的工厂和产品类即可。

单例模式(Singleton)

意图:保证一个类仅只有一个实例,并提供一个他的全局访问点

# 单例类 - Logger
class Logger:_instance = Nonedef __init__(self):# 私有构造函数,防止外部实例化pass@classmethoddef get_instance(cls):if not cls._instance:cls._instance = Logger()return cls._instancedef log(self, message):print("Logging:", message)# 客户端代码
logger1 = Logger.get_instance()
logger1.log("Message 1")  # 输出:Logging: Message 1logger2 = Logger.get_instance()
logger2.log("Message 2")  # 输出:Logging: Message 2print(logger1 is logger2)  # 输出:True,两个实例是同一个对象

在这个示例中,Logger是单例类,它的构造函数被声明为私有,以防止外部实例化。通过get_instance方法获取该类的唯一实例。客户端代码通过调用get_instance方法来获取日志记录器的实例,并调用其log方法记录日志。由于单例模式的特性,无论多少次调用get_instance方法,都会返回同一个实例。

构建器模式(Builder)

意图:将对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。

# 产品类 - 电脑
class Computer:def __init__(self):self.cpu = Noneself.memory = Noneself.storage = Nonedef set_cpu(self, cpu):self.cpu = cpudef set_memory(self, memory):self.memory = memorydef set_storage(self, storage):self.storage = storagedef display(self):print(f"CPU: {self.cpu}")print(f"Memory: {self.memory}")print(f"Storage: {self.storage}")# 建造者接口
class Builder:def build_cpu(self):passdef build_memory(self):passdef build_storage(self):passdef get_computer(self):pass# 具体建造者类 - 游戏电脑建造者
class GamingComputerBuilder(Builder):def __init__(self):self.computer = Computer()def build_cpu(self):self.computer.set_cpu("Intel i7")def build_memory(self):self.computer.set_memory("16GB DDR4")def build_storage(self):self.computer.set_storage("1TB SSD")def get_computer(self):return self.computer# 具体建造者类 - 办公电脑建造者
class OfficeComputerBuilder(Builder):def __init__(self):self.computer = Computer()def build_cpu(self):self.computer.set_cpu("Intel i5")def build_memory(self):self.computer.set_memory("8GB DDR4")def build_storage(self):self.computer.set_storage("500GB HDD")def get_computer(self):return self.computer# 指挥者类
class Director:def __init__(self, builder):self.builder = builderdef construct(self):self.builder.build_cpu()self.builder.build_memory()self.builder.build_storage()# 客户端代码
gaming_builder = GamingComputerBuilder()
director = Director(gaming_builder)
director.construct()
gaming_computer = gaming_builder.get_computer()
gaming_computer.display()office_builder = OfficeComputerBuilder()
director = Director(office_builder)
director.construct()
office_computer = office_builder.get_computer()
office_computer.display()

在上面的示例中,Computer 是产品类,表示电脑对象。Builder 是建造者接口,定义了构建电脑的方法。GamingComputerBuilderOfficeComputerBuilder 是具体建造者类,分别负责构建游戏电脑和办公电脑。Director 是指挥者类,负责控制建造过程。

客户端代码可以通过创建不同的具体建造者实例,并通过指挥者来构建不同类型的电脑对象。使用建造者模式可以方便地构建具有不同配置的电脑对象,而无需直接调用产品类的构造函数或者繁琐地设置每个属性。

原型模式(Prototype)

意图:原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象

import copy# 原型接口
class Prototype:def clone(self):pass# 具体原型类 - 矩形
class Rectangle(Prototype):def __init__(self, width, height):self.width = widthself.height = heightdef set_width(self, width):self.width = widthdef set_height(self, height):self.height = heightdef clone(self):return copy.deepcopy(self)def __str__(self):return f"Rectangle [width={self.width}, height={self.height}]"# 客户端代码
if __name__ == '__main__':# 创建原型对象rectangle = Rectangle(10, 20)# 克隆原型对象cloned_rectangle = rectangle.clone()print("Cloned Rectangle:", cloned_rectangle)# 修改克隆对象的属性cloned_rectangle.set_width(15)cloned_rectangle.set_height(25)print("Modified Cloned Rectangle:", cloned_rectangle)

Python 中,我们使用 copy 模块的 deepcopy() 函数来实现对象的深拷贝,以克隆原型对象。然后,我们可以通过调用克隆对象的方法来修改其属性。

需要注意的是,Python 中的类不需要显式地实现接口,因此我们只需定义一个基类 Prototype,然后在具体原型类 Rectangle 中重写 clone() 方法即可。

附加知识

1. python中@classmethod是什么,有什么作用

(使用方式等同于Java中的静态方法)
在 Python 中,@classmethod 是一个装饰器(Decorator),用于定义一个类方法。类方法是与类相关联的方法,而不是与类的实例相关联的方法。

类方法使用装饰器 @classmethod 来标识,并且第一个参数通常被命名为 cls,表示类本身。与实例方法不同,类方法可以直接通过类名调用,而不需要创建类的实例。

@classmethod 的作用主要有以下几点:

  • 允许在不创建类的实例的情况下调用方法。这对于执行与类相关的操作非常有用,例如创建类级别的工厂方法。
  • 允许在类方法内部访问类的属性和其他类方法,而无需使用实例。
  • 提供一种约定,使得代码更易读和理解,因为它明确指示该方法是一个类方法。
    下面是一个使用 @classmethod 定义类方法的示例:
class MyClass:count = 0def __init__(self):MyClass.count += 1@classmethoddef get_instance_count(cls):return cls.count

在上面的示例中,get_instance_count() 是一个类方法,它返回创建的 MyClass 实例的数量。你可以通过类名直接调用该方法,而不需要创建类的实例:

print(MyClass.get_instance_count())  # 输出: 0obj1 = MyClass()
print(MyClass.get_instance_count())  # 输出: 1obj2 = MyClass()
print(MyClass.get_instance_count())  # 输出: 2

2. python中类中的__init__ 和其他方法执行顺序

在 Python 中,类的方法执行顺序主要取决于方法的调用方式和继承关系。下面是一般情况下常见的执行顺序:

定义类时,会先执行类体中的代码,包括属性和方法的定义。
当创建类的实例时,会自动调用__init__方法作为构造函数,用于初始化对象的属性。
在实例化过程中,如果类定义了父类,则会先执行父类的 __init__ 方法,然后再执行子类的 __init__ 方法。
在实例化后,可以通过实例调用类的其他方法。
以下是一个示例来说明执行顺序:

class Parent:def __init__(self):print("Parent's __init__")def method(self):print("Parent's method")class Child(Parent):def __init__(self):super().__init__()  # 先执行父类的 __init__print("Child's __init__")def method(self):super().method()  # 调用父类的 methodprint("Child's method")child = Child()

输出结果:

Parent's __init__
Child's __init__

在上面的示例中,首先创建了 Child 类的实例 child。在实例化过程中,由于 Child 类继承自 Parent 类,因此会先执行父类 Parent__init__方法,然后再执行子类 Child__init__方法。最终输出结果中可以看到先打印了 “Parent’s init”,然后才打印了 “Child’s init”。

深拷贝(Deep Copy)和浅拷贝(Shallow Copy)

浅拷贝是指创建一个新对象,然后将原始对象的非静态字段的值复制到新对象中。如果字段是基本类型,则复制其值;如果字段是引用类型,则复制引用而不是实际对象。这意味着原始对象和浅拷贝对象会共享相同的引用对象。如果修改了引用对象的属性,则原始对象和浅拷贝对象都会受到影响。

深拷贝是指创建一个新对象,并递归地复制原始对象及其所有引用对象的内容。这意味着原始对象和深拷贝对象拥有相同的值,但是它们引用的是不同的对象实例。修改深拷贝对象或其引用对象的属性不会影响原始对象。

下面是一个示例来说明浅拷贝和深拷贝的区别:

import copyclass Person:def __init__(self, name, address):self.name = nameself.address = addressdef setAddress(self, address):self.address = addressdef getAddress(self):return self.addressdef __str__(self):return f"Person [name={self.name}, address={self.address}]"class Address:def __init__(self, city):self.city = citydef setCity(self, city):self.city = citydef __str__(self):return f"Address [city={self.city}]"# 客户端代码
if __name__ == "__main__":address = Address("New York")person1 = Person("John", address)# 浅拷贝person2 = copy.copy(person1)print("Shallow Copy:")print("person1:", person1)print("person2:", person2)# 修改引用对象的属性person2.getAddress().setCity("London")print("After modifying reference object:")print("person1:", person1)print("person2:", person2)print()# 深拷贝deepCopiedAddress = Address(address.getCity())person3 = Person(person1.name, deepCopiedAddress)print("Deep Copy:")print("person1:", person1)print("person3:", person3)# 修改引用对象的属性person3.getAddress().setCity("Paris")print("After modifying reference object:")print("person1:", person1)print("person3:", person3)

在上述代码中,我们定义了一个 Person 类和一个 Address 类。Person 类包含一个引用类型字段 address,它引用了 Address 对象。

首先,我们创建了一个原始对象 person1,然后通过浅拷贝创建了一个新对象 person2。可以看到,person1person2 的引用字段 address 指向同一个 Address 对象。因此,当我们修改 person2 的引用对象的属性时,person1 的引用对象也会受到影响。

接下来,我们使用深拷贝创建了 person3 对象。通过 copy.copy() 函数复制 person1 对象时,只是创建了一个新的 Person 对象,但仍然共享相同的 Address 对象。而通过 copy.deepcopy() 函数进行深拷贝时,会递归地复制所有引用对象的内容,因此 person3 的引用对象是一个新的 Address 对象。

最后,我们修改了 person3 的引用对象的属性,并打印出原始对象 person1 和深拷贝对象 person3,可以看到它们的引用对象不再共享相同的值。

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

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

相关文章

CompletableFuture常见方法以及使用

常用场景: 1.并行执行多个任务:CompletableFuture 可以用于并行执行多个任务,从而提高性能 2.并行执行多个任务:CompletableFuture 可以用于并行执行多个任务,从而提高性能 3.任务依赖和组合:Completabl…

计算机网络基础二

课程目标 了解 OSI 七层模型分层结构 了解 TCP/IP 协议簇四层模型分层结构 能够说出 TCP/IP 协议簇中 运输层、网络层和数据链路 层常见的 相关协议 能够说出 TCP/IP 的三次握手四次断开过程 了解 Vmware 的三种网络模式 能够使用客户端工具连接虚拟机 掌握主机名、 DNS…

Android S从桌面点击图标启动APP流程 (六)

系列文章 Android S从桌面点击图标启动APP流程 (一)Android S从桌面点击图标启动APP流程 (二) Android S从桌面点击图标启动APP流程 (三) Android S从桌面点击图标启动APP流程 (四) Android S从桌面点击图标启动APP流程 (五) Android 12的源码链接: android 1…

【OpenCV实现图像的算数运算,性能测试和优化,改变颜色空间】

文章目录 OpenCV功能概要图像的算数运算性能测试和优化改变颜色空间对象追踪 OpenCV功能概要 OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉和机器学习库,提供了丰富的图像处理和计算机视觉算法。它支持多种编程语言&…

js中数组的相关方法

引言: 数组(Array)是有序的元素序列。 [1]若将有限个类型相同的变量的集合命名,那么这个名称为数组名。组成数组的各个变量称为数组的分量,也称为数组的元素,有时也称为下标变量 方法: push()…

数字化转型系列主题:战略咨询常用术语解释和样例说明

引言 做战略咨询的人经常提到一些术语 ”某企业的愿景,某企业的价值观,战略目标,举措,行动,挑战,...“, 这些术语对刚进入咨询行业的小白或其它行业的人,经常会分不清楚,弄的一头雾…

Java中的volatile关键字

volatile是什么? "volatile"是一个关键字,用于修饰变量。它的作用是告诉编译器该变量可能会在意料之外的时候被修改,因此编译器在对该变量进行优化时需要特别小心。 具体来说,当一个变量被声明为"volatile"…

dd命令用法学习,是一个功能强大的工具

dd 命令是一个功能强大的工具,它有许多参数可以用来控制其行为。以下是 dd 命令中常用的一些参数: - ifinputfile:指定输入文件的路径。 - ofoutputfile:指定输出文件的路径。 - bssize:设置每个块的大小。可以使用不同…

探索控制领域:从电视遥控器到自动驾驶【基础概念理解、应用实例】

当谈到控制学和控制系统时,你可能会联想到电视遥控器、自动驾驶汽车、飞机自动驾驶系统以及许多其他自动化系统。但控制学是一个更广泛的学科,它涵盖了各种领域,从工程到生物学,从经济学到环境科学。让我们深入了解控制学的基本概…

算法通过村第十七关-贪心|白银笔记|贪心高频问题

文章目录 前言区间问题判断区间是否重复合并区间插入区间 字符串分割加油站问题总结 前言 提示:如果生活把你的门关上了,那你就再打开,这就是门,门就是这样的。 --佚名 贪婪的思想不一定要理解的很透彻,但是贪婪的问题…

Cookie技术

Cookie中文名称为小型文本文件,指某些网站为了辨别用户身份、进行会话跟踪而储存在用户本地终端上的数据。 Cookie是由服务器端生成,发送给User-Agent(—般是浏览器),浏览器会将Cookie的key/value保存到某个目录下的文…

软考系列(系统架构师)- 2013年系统架构师软考案例分析考点

试题一 软件架构(根据描述填表、ESB 定义和功能) 【问题1】(10分) 服务建模是对Ramp Coordination信息系统进行集成的首要工作,公司的架构师首先对Ramp Coordination信息系统进行服务建模,识别出系统中的两…

从Mysql架构看一条查询sql的执行过程

1. 通信协议 我们的程序或者工具要操作数据库,第一步要做什么事情? 跟数据库建立连接。 首先,MySQL必须要运行一个服务,监听默认的3306端口。在我们开发系统跟第三方对接的时候,必须要弄清楚的有两件事。 第一个就是通…

uniapp开发小程序 小米手机真机bottom:0无效 底部间隙 设备安全区域处理办法

uniApp自定义导航 CSS设置 bottom:0竟然无效,而iphone和开发模拟器没有问题 height: 150rpx;position: fixed;left: 0;right: 0;bottom: calc(var(--window-bottom,0)); 网上查了各种方法,包括设置bottom:-20啊以及 padding-bottom: constant(safe-are…

centos jdk 安装

1、oracle官网下载jdk8 https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html 2、楼主用的以前下载好的安装包jdk-8u111-linux-x64.gz。下载后使用工具如Xftp将安装包上传到/opt目录下,这里随便什么目录都行,并解压安装包。 c…

静电模型PIC方法的Matlab仿真设计

任务要求: 采用PIC模拟方法的静电模型来模拟多环形电子注在圆柱系统中的运动轨迹。模拟电子枪阴极表面发射电子注,电子在静态场的作用下运动直至稳定的运动过程。其中 系统长:0.01m 系统半径:0.005m 入射的每个宏电子电流&#x…

业界中说的快速原型法是什么

快速原型法是一种软件开发过程,其核心思想是在开发初期快速构建一个系统的原型,即一个工作模型,以便用户和开发者能够更好地理解系统的需求和功能。这种方法强调快速迭代和用户参与,目的是更早地发现和修正问题,从而提…

C#中通过BeginInvoke()和EndInvoke()来实现异步

.NET Framework允许异步调用任何方法。定义与需要调用的方法具有相同签名的委托;公共语言运行库将自动为该委托定义具有适当签名的 BeginInvoke 和 EndInvoke 方法。以下介绍C#中,通过BeginInvoke()和EndInvoke()来实现异步。 1、异步编程 调用BeginInv…

Ubuntu中查看电脑有多少个核——lscpu

1. 使用lscpu命令: 打开终端并输入以下命令: lscpu你会看到与CPU相关的详细信息。查找"CPU(s)"这一行来看总的核心数。另外,“Core(s) per socket”表示每个插槽或每个物理CPU的核数,“Socket(s)”表示物理CPU的数量。将这两个值相乘即得到总…

IO流框架,缓冲流

一.缓冲流有什么优点 Java中的缓冲流(Buffered Stream)具有以下优势: 提高效率:缓冲流通过在内存中缓存一部分数据,减少了直接从内存到磁盘或从磁盘到内存的频繁IO操作,从而提高了读写效率。缓冲区大小调整…