设计模式之创建型模式
创建型模式与对象的创建有关。 创建型模式抽象了对象实例化的过程,这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 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
是抽象产品类,Circle
和Rectangle
是具体产品类。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
是抽象工厂接口,定义了创建产品的方法。BenzFactory
和 BMWFactory
是具体工厂实现类,分别负责创建奔驰和宝马汽车的引擎和轮胎。Engine
和 Tire
是抽象产品接口,定义了产品的方法。BenzEngine
、BMWEngine
、BenzTire
和 BMWTire
是具体产品类,分别实现了奔驰和宝马汽车的引擎和轮胎。
客户端代码可以通过传入不同的具体工厂实例来创建不同品牌的汽车引擎和轮胎,并使用它们的方法。
抽象工厂方法模式可以方便地扩展产品系列,例如添加奥迪工厂和奥迪产品类,只需要新增对应的工厂和产品类即可。
单例模式(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
是建造者接口,定义了构建电脑的方法。GamingComputerBuilder
和 OfficeComputerBuilder
是具体建造者类,分别负责构建游戏电脑和办公电脑。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
。可以看到,person1
和 person2
的引用字段 address
指向同一个 Address
对象。因此,当我们修改 person2
的引用对象的属性时,person1
的引用对象也会受到影响。
接下来,我们使用深拷贝创建了 person3
对象。通过 copy.copy()
函数复制 person1 对象时,只是创建了一个新的 Person
对象,但仍然共享相同的 Address
对象。而通过 copy.deepcopy()
函数进行深拷贝时,会递归地复制所有引用对象的内容,因此 person3
的引用对象是一个新的 Address
对象。
最后,我们修改了 person3
的引用对象的属性,并打印出原始对象 person1
和深拷贝对象 person3
,可以看到它们的引用对象不再共享相同的值。
相关文章:
设计模式之创建型模式
创建型模式与对象的创建有关。 创建型模式抽象了对象实例化的过程,这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。创建型模式有以下 工厂模式(Factory Method) 意图…...

使用jdbc技术连接数据库
连接数据库 <dependencies><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.28</version><scope>compile</scope></dependency> </dependencies> g…...
OpenLayers入门,快速搭建vue+OpenLayers地图脚手架项目
专栏目录: OpenLayers入门教程汇总目录 前言 本章针对Vue初学者,对Vue不熟悉,甚至还不会Vue的入门学生读者。 本章会详细讲解从NodeJS环境到npm环境的各个步骤,再到使用vue-cli脚手架快速生成项目,以及添加OpenLayers地图库依赖,编写简单的xyz高德地图显示页面的完整教…...
完成比写得好更重要,先完成初稿再说
我发现自己有个毛病,总想着满意了才动手。于是,经常做到一半跑去看文献,然后陷入文献中觉得这个比自己好,那个比自己好。于是,暂时中断手边工作,最后进度被推迟,甚至啥也没做出来。 今晚再次听…...
Spring boot 处理复杂json接收,同种类型、不同场景处理
场景: json大体格式一致,但是 ext_info 扩展字段对象,场景不同字段不同根据某字段类型,不同值,对应不同实现的 Component,处理不同场景这里根据 event,来做不同处理 {"data": {"event"…...
排列置换环上构造:1025T3
http://cplusoj.com/d/senior/p/SS231025C 排列构造的新知识:上置换环! 我们发现朴素做法是 n 2 n^2 n2 级别的,但数据范围希望我们是 n 2 2 \frac {n^2}2 2n2 级别的。我们发现我们暴力复制序列显得非常蠢,因为很多序列前后…...
Stable diffusion的一些参数意义及常规设置
在线stabel Diffusion模型 https://huggingface.co/spaces/stabilityai/stable-diffusion 随机种子 seed 如果想要同一个文本提示,生成多次都是同一图像,可以设置一个随机种子,类似于random.seed()的原理,并将生成器传递给管道。…...
成员变量、静态成员变量、局部变量、常量的内存区域
一、Java中没有全局变量的概念,变量分为类的成员变量、静态成员变量和方法中的局部变量 1、局部变量,基本类型的局部变量变量名和值都存放在虚拟机栈中,引用类型的局部变量变量名存放在栈中,而变量指向的对象存放在堆中。记也很好…...

网络协议--IGMP:Internet组管理协议
13.1 引言 12.4节概述了IP多播给出,并介绍了D类IP地址到以太网地址的映射方式。也简要说明了在单个物理网络中的多播过程,但当涉及多个网络并且多播数据必须通过路由器转发时,情况会复杂得多。 本章将介绍用于支持主机和路由器进行多播的In…...

网络安全https
http是明文的,相当于在网上裸奔,引出了https,大多数网站都转为了https,连非法的赌博网站有的都是https的。 1.https的网站是不是必须让用户装数字证书? 答:分两种,一种是单向认证,像…...
xcode Simulator 手动安装
xcode Simulator 手动安装 参考文档 xcode又又又升级了,升级完成之后不下载最新的 iOS 17 Simulator就不能编译运行了,只能静静的等他下载。但是离谱的是这个居然没有断点续下,每次都要重新下载,眼睁睁的看着下载了4个G然后断掉…...

Unity中国、Cocos为OpenHarmony游戏生态插上腾飞的翅膀
2023年是OpenHarmony游戏生态百花齐放的一年!为了扩展OpenHarmony游戏生态,OpenHarmony在基金会成立了游戏SIG小组,游戏SIG小组联合cocos,从cocos2dx入手一周内快速适配了cocos2.2.6的MVP版本,随后又分别适配了cocos2d…...
Monaco Editor编辑器
monaco-editor Monaco Editor 是一个基于浏览器的代码编辑器,由微软开发。它提供了丰富的功能,包括语法高亮、智能代码补全、代码折叠、多光标编辑等。Monaco Editor 是 Visual Studio Code 的核心编辑器,也被广泛用于其他开发工具和在线代码…...

ARM | 传感器必要总线IIC
IIC总线介绍 1.谈谈你对IIC总线理解? 1)IIC总线是串行半双工同步总线,主要用于连接整体电路 2)SCL/SDA作用:IIC是两线制,一根是时钟线SCK,用于控制什么时候进行进行数据传输,时钟信号由主机发出; 另一根是数据线SDA,用于进行数据传输,可以从…...

Mybatis中Resources和ClassLoaderWrapper
ClassLoaderWrapper Resources...

Linux多线程服务端编程:使用muduo C++网络库 学习笔记 第三章 多线程服务器的适用场合与常用编程模型
本文中的多线程服务器指运行在Linux上的独占式网络应用程序。硬件平台为Intel x86-64系列的多核CPU,单路或双路SMP(Symmetric Multi-Processing,对称多处理,它是一种多核处理器架构,其中多个CPU核心共享系统的内存和其…...

windows下使用FFmpeg开源库进行视频编解码完整步聚
最终解码效果: 1.UI设计 2.在控件属性窗口中输入默认值 3.复制已编译FFmpeg库到工程同级目录下 4.在工程引用FFmpeg库及头文件 5.链接指定FFmpeg库 6.使用FFmpeg库 引用头文件 extern "C" { #include "libswscale/swscale.h" #include "libavdevic…...

如何更改eclipse的JDK版本
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 一、有时候导入一些网上的资源需要更换JDK二、使用步骤1. 总结 一、有时候导入一些网上的资源需要更换JDK 具体操作如下 二、使用步骤 1. 在eclipse上方工具栏找…...

HarmonyOS第一课运行Hello World
前言 俗话说,工欲善其事必先利其器。鸿蒙第一课,我们先从简单的Hello World运行说起。要先运行Hello World,那么我们必须搭建HarmonyOS的开发环境。 下载与安装DevEco Studio 在HarmonyOS应用开发学习之前,需要进行一些准备工作&a…...
解决el-tooltip滚动时悬浮框相对位置发生变化
获取最外层box的class,并在内层添加el-scrollbar <template><div class"ChartsBottom"><el-scrollbar><ul class""><li v-for"(item, index) in list" :key"index"><div class"con…...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...

使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...

力扣热题100 k个一组反转链表题解
题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...

Visual Studio Code 扩展
Visual Studio Code 扩展 change-case 大小写转换EmmyLua for VSCode 调试插件Bookmarks 书签 change-case 大小写转换 https://marketplace.visualstudio.com/items?itemNamewmaurer.change-case 选中单词后,命令 changeCase.commands 可预览转换效果 EmmyLua…...

一些实用的chrome扩展0x01
简介 浏览器扩展程序有助于自动化任务、查找隐藏的漏洞、隐藏自身痕迹。以下列出了一些必备扩展程序,无论是测试应用程序、搜寻漏洞还是收集情报,它们都能提升工作流程。 FoxyProxy 代理管理工具,此扩展简化了使用代理(如 Burp…...