[Python进阶] 类的设计模式

4.11 设计模式

在Python中,类的设计模式是指一种通用的解决方案或设计模板,针对特定的问题或需求构建类结构,并提供相关的方法和属性。这些设计模式可以帮助开发人员遵循最佳实践、提高代码质量、增强可读性、降低维护成本。
需要注意的是,类设计模式不是语言特定的,这些模式同样适用于其他面向对象编程语言。
在 Python 中,有多种常见的类设计模式。下面将进行介绍。

4.11.1 工厂模式(Factory Pattern)

用于创建对象实例的模式,简化了对象的创建过程。
在Python中,类设计的工厂模式是一种创建对象的方法。它可以使用一个公共接口来创建不同的对象,这些对象通常共享相同的属性和行为。更具体地说,工厂模式可以通过使用过程化编程技术和面向对象编程技术来实现。
下面是一个示例:

class Dog:def __init__(self, name):self._name = namedef speak(self):return "Woof!"class Cat:def __init__(self, name):self._name = namedef speak(self):return "Meow!"def get_pet(pet="dog"):pets = dict(dog=Dog("Hope"), cat=Cat("Peace"))return pets[pet]dog = get_pet("dog")
print(dog.speak())cat = get_pet("cat")
print(cat.speak())

在上述代码中,我们定义了两个类:Dog和Cat,每个类都有它自己的__init__函数和speak方法。然后我们定义了一个get_pet函数,该函数接收一个参数pet并根据传递的值获取pets字典中对应的实例赋值给dog或者cat变量中。
如果传入的pet参数是"dog",那么将调用get_pet函数,并返回一个Dog对象;而如果传入的参数是"cat",那么会返回一个Cat对象。最后我们分别调用了dog.speak()和cat.speak()方法输出其对应的声音。
这种设计模式对于以下情况非常有用:

  • 当我们需要隐藏对象创建的实现细节时。
  • 当我们希望将对象的创建与使用分开时。
  • 当我们想要通过公共接口在运行时确定对象类型时。

4.11.2 单例模式(Singleton Pattern)

确保类只能有一个实例,并提供对该实例的全局访问点。
在Python中,类设计的单例模式是指一个类只有一个实例对象。这意味着无论如何调用该类,在内存中只会存在同一个实例对象,如果再次创建该类的实例对象时,将返回已经存在的那个。这可以避免在程序中多次创建相同的对象,节省资源和提高性能。
下面是一个示例:

class Singleton:__instance = Nonedef __new__(cls, name):if cls.__instance is None:cls.__instance = super().__new__(cls)cls.__instance.__initialized = Falsereturn cls.__instancedef __init__(self, name):if not self.__initialized:self.name = nameself.__initialized = Truedef say_hello(self):print(f"Hello, I am {self.name} ({id(self)})!")

在上述代码中,我们定义了一个名为Singleton的类,使用了双重判断的方式确保仅创建一个实例对象。在类的__new__方法中,如果没有创建过实例对象,就通过super()调用父类的__new__方法来创建一个实例对象,并将它赋值给__instance属性。若已经创建过实例对象,则直接返回现有的那个实例对象。同时类中的__initialized属性用于确保__init__方法只执行一次。

我们还在类的__init__方法中添加了一个名为name的属性,以标识该实例的名称。最后我们定义了一个say_hello方法,用于输出实例的名称和其在内存中的地址。
下面展示如何使用Singleton类:

dog1 = Singleton("Hope")
dog2 = Singleton("Peace")print(id(dog1))
print(id(dog2))dog1.say_hello()
dog2.say_hello()print(dog1 == dog2)

在上述代码中,我们先创建了两个实例对象dog1dog2,采用不同的name,但是由于是单例模式,只有第一次的name能够正常赋值,dog2的name则无法再次赋值。最后得到的结果就显示dog1和dog2的id一致,say_hello函数返回的也一致。

4.11.3 观察者模式(Observer Pattern)

在对象之间建立一对多的依赖关系,以便当一个对象状态更改时通知其所有依赖项。
在Python中,类设计的观察者模式是指当一个对象的状态发生改变时,所有依赖它的其他对象都会得到通知并自动更新。
下面是一个示例:

class Observer:def update(self, obj, *args, **kwargs):passclass Observable:def __init__(self):self._observers = []def addObserver(self, observer):if observer not in self._observers:self._observers.append(observer)def removeObserver(self, observer):if observer in self._observers:self._observers.remove(observer)def notifyObservers(self, obj, *args, **kwargs):for observer in self._observers:observer.update(obj, *args, **kwargs)class Dog(Observable):def __init__(self, name="dog"):super().__init__()self._name = namedef setName(self, name):self._name = nameself.notifyObservers(self)def getName(self):return self._nameclass Owner(Observer):def __init__(self, name):self._name = namedef update(self, dog, *args, **kwargs):print(f"{self._name}: {dog.getName()} seems happy today!")dog1 = Dog("Hope")
dog2 = Dog("Peace")
owner1 = Owner("Alice")
owner2 = Owner("Bob")dog1.addObserver(owner1)
dog1.addObserver(owner2)
dog2.addObserver(owner2)dog1.setName("Happy Hope")
dog2.setName("Peaceful Peace")

在上面的代码中,我们定义了两个类ObserverObservable,以及继承自ObservableDog类和继承自ObserverOwner类。
Observer类中定义了名称为update的方法,这是观察者需要实现的方法。在本例中,我们没有在其中写入任何代码。 Observable 类实现了添加、删除和通知观察者对象的方法,其中addObserver将要添加的观察者放入到观察者列表中。此时观察者可以根据removeObserver则是从观察者列表中剔除。notifyObservers则是通知观察者。

4.11.4 适配器模式(Adapter Pattern)

将接口转换为其他接口,以兼容客户端代码的需求。
适配器模式是一种设计模式,它允许我们将一个类的接口转换为另一个客户端所期望的接口。在Python中,适配器模式的实现方式通常涉及到继承和组合两种方式。
下面是一个使用继承实现适配器模式的示例,假设我们有两个类,一个是Adaptee类,具有不同于目标客户端所期望的接口:

class Adaptee:def specific_request(self):return "adaptee code"

和一个客户端所期望的接口Target:

class Target:def request(self):pass

我们可以通过TargetClassAdapter类来使Adaptee与Target兼容,适配器将Adaptee的方法调用转换成Target客户端所期望的接口:

class TargetClassAdapter(Target, Adaptee):def request(self):return self.specific_request()

这样一来,客户端就可以使用TargetClassAdapter类来调用Adatpee的方法,同时符合Target接口规范:

if __name__ == "__main__":target = TargetClassAdapter()result = target.request()print(result)  # 'adaptee code'

同样,我们也可以使用组合方式来实现适配器模式。

4.11.5 组合模式(Composite Pattern)

通过将对象组合成树形结构,使得单个对象和组合对象都可以按统一的方式进行处理。
在Python中,组合模式指的是将对象组合成树形结构以表示“部分-整体”的层次结构。组合能让客户端以一致的方式处理个别对象以及对象组合。
以下是使用Python实现组合模式的简单示例:

class Component:def __init__(self, name):self.name = namedef add(self, component):passdef remove(self, component):passdef display(self, depth):passclass Leaf(Component):def add(self, component):print("Cannot add to a leaf")def remove(self, component):print("Cannot remove from a leaf")def display(self, depth):print("-" * depth + self.name)class Composite(Component):def __init__(self, name):super().__init__(name)self.children = []def add(self, component):self.children.append(component)def remove(self, component):self.children.remove(component)def display(self, depth):print("-" * depth + self.name)for child in self.children:child.display(depth + 2)if __name__ == "__main__":root = Composite("root")root.add(Leaf("leaf A"))root.add(Leaf("leaf B"))comp = Composite("Composite X")comp.add(Leaf("leaf XA"))comp.add(Leaf("leaf XB"))root.add(comp)root.display(1)

在这个例子中,Component类代表组合模式中的组件,其中包含了添加、删除和显示其内容的方法。Leaf类代表叶节点,不能够包含其他的组件。Composite类代表组合节点,包含了多个子组件。
在这个示例中,创建了一个root组合节点,包含两个叶节点Leaf ALeaf B以及一个名为Composite X的子组合节点。调用display()方法时,将按树形结构递归地显示所有组件的内容。

4.11.6 策略模式(Strategy Pattern)

定义算法族,使它们之间可以互相替换,而不会影响到客户端的使用。
类的策略模式是一种设计模式,它允许在运行时选择算法的不同变体或行为。这个模式中,我们将不同的算法或策略封装成不同的类并让他们可以相互替换。
以下是一个简单的代码示例:

class Strategy:def do_algorithm(self, data):passclass ConcreteStrategyA(Strategy):def do_algorithm(self, data):return sorted(data)class ConcreteStrategyB(Strategy):def do_algorithm(self, data):return list(reversed(sorted(data)))class Context:def __init__(self, strategy: Strategy):self._strategy = strategydef execute_strategy(self, data):return self._strategy.do_algorithm(data)if __name__ == "__main__":context_a = Context(ConcreteStrategyA())result_a = context_a.execute_strategy([1, 3, 2])print(result_a)context_b = Context(ConcreteStrategyB())result_b = context_b.execute_strategy([1, 3, 2])print(result_b)

在这个例子中,我们定义了一个Strategy的基类和两个具体的策略类:ConcreteStrategyAConcreteStrategyB。每个策略类都实现了do_algorithm方法,并分别提供了不同的实现。
接着我们定义了一个上下文类Context用来执行策略并生成所需的结果。这个类包含一个指向Strategy对象的引用,并在执行方法时将数据传递给所选的策略类进行处理。
最后我们可以创建不同的上下文对象,并通过方法的多态性执行相应的策略。这样就可以实现运行时动态选择算法行为的目的。

4.11.7 装饰器模式(Decorator Pattern)

动态地向对象添加额外的行为,而无需修改原始类的代码。
在Python中,类设计的装饰器模式是指,使用装饰器来修改一个类的行为或属性,而不必直接修改该类的原始定义。这可以使代码更加灵活和可维护。
以下是一个实例,其中定义了一个名为Logger的装饰器,它可以添加记录方法到一个类中:

def Logger(cls):"""A decorator that adds logging functionality to a class"""# Define the logging methoddef log(self, message):print(f"{self.__class__.__name__}: {message}")# Add the logging method to the classcls.log = log# Return the modified classreturn cls# Define a class with the Logger decorator
@Logger
class MyClass:pass# Use the class and its logging method
my_object = MyClass()
my_object.log("Hello World!")

在上面的示例中,Logger函数作为一个装饰器来使用,用于增加Python类的日志功能。当我们在类定义之前应用此装饰器时,Logger函数将自动被调用并向该类添加log方法,这个方法可以访问该类的名称和任何传递给它的消息。因此,在创建MyClass对象后,我们可以调用该对象的log方法,并输出一条带有类的名称和消息的日志信息。

4.11.8 建造者模式(Builder Pattern)

将复杂的对象构建与其表示分离,以便不同的表示方式可以用于该对象进行构建。
类设计的建造者模式是一种创建复杂对象的设计模式,它使用多个简单的对象逐步构建出一个复杂的对象。这种模式是将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。
在Python中,可以用下面的示例演示建造者模式。

class Pizza:def __init__(self, dough='', sauce='', toppings=[]):self.dough = doughself.sauce = sauceself.toppings = toppingsdef __str__(self):return 'dough: {}, sauce: {}, toppings: {}'.format(self.dough, self.sauce, ', '.join(self.toppings))class PizzaBuilder:def __init__(self):self.pizza = Pizza()def set_dough(self, dough):self.pizza.dough = doughreturn selfdef set_sauce(self, sauce):self.pizza.sauce = saucereturn selfdef add_topping(self, topping):self.pizza.toppings.append(topping)return selfdef build(self):return self.pizzaclass MargheritaPizzaBuilder(PizzaBuilder):def __init__(self):super().__init__()self.pizza.dough = 'thin'self.pizza.sauce = 'tomato'def add_toppings(self):self.pizza.toppings.extend(['mozzarella', 'basil'])return selfclass PepperoniPizzaBuilder(PizzaBuilder):def __init__(self):super().__init__()self.pizza.dough = 'pan'self.pizza.sauce = 'tomato'def add_toppings(self):self.pizza.toppings.extend(['mozzarella', 'pepperoni'])return selfclass Director:def __init__(self, builder=None):self.builder = builderdef set_builder(self, builder):self.builder = builderdef construct_pizza(self):if not self.builder:raise ValueError("Builder is not set")self.builder.add_topping().build()

在这个例子中,PizzaBuilder类可以创建定制并返回Pizza。MargheritaPizzaBuilder、PepperoniPizzaBuilder则创建特定的Pizza。Director类则可以通过传入不同的PizzaBuilder构建出不同的Pizza。使得同样的构建过程创建出不同的Pizza。

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

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

相关文章

房屋结构健康监测,科技助力让建筑更安全

房屋建筑是人们赖以生存的场所,然而当前我国许多房屋已经达到了使用寿命的中期,房屋的安全系数逐年降低,风险也随着时间的推移而累积。长期以来,我国的房屋普遍存在寿命短、隐患多的问题,“重建设,轻管理”…

探索PDF校对:为何这是现代数字文档的关键步骤

在今日的数字化浪潮中,文档的创建与分享从未如此频繁。尤其是PDF,作为一个普遍接受的标准文件格式,其在企业、学术和日常生活中的应用已经无处不在。但随之而来的挑战是如何确保文档的准确性和专业性。让我们深入探索PDF校对的重要性以及它为…

[国产MCU]-W801开发实例-定时器

定时器 文章目录 定时器1、定时器介绍2、定时器驱动API3、定时器使用示例本文将详细介绍如何使用W801的定时器模块。 1、定时器介绍 W801的定时器包含一个32-bit自动加载的计数器,该计数器由系统时钟经过分频后驱动。 W801有 6路完全独立定时器。实现了精确的定时时间以及中断…

element-ui中的el-table的summary-method(合计)的使用

场景图片: 图片1: 图片2: 一:使用element中的方法 优点: 直接使用summary-method方法,直接,方便 缺点: 只是在表格下面添加了一行,如果想有多行就不行了 1:h…

一、MQ的基本概念

1、初识MQ MQ全称是Message Queue,消息队列,多用于系统之间进行异步通信。队列的概念数据结构中有详细介绍过,先进先出,消息队列就是存储消息的数据结构。 同步调用和异步调用两者之间的区别: 同步调用:发…

opencv-dnn

# utils_words.txt 标签文件 import osimage_types (".jpg", ".jpeg", ".png", ".bmp", ".tif", ".tiff")def list_images(basePath, containsNone):# return the set of files that are validreturn list_file…

gdb 条件断点

条件断点,顾名思义就是有条件才会触发的断点,一般设置此类断点形如:b xxx if xxx,如: 要触发此断点则需要 is_created 0。打完断点我们也可以用 info b 查看一下当前已经设置的断点信息,如: 断…

【JavaSE】接口

文章目录 接口的概念接口的使用接口的特性实现多个接口接口间的继承抽象类和接口的区别 接口的概念 接口就是公共的行为规范标准,大家在实现时,只要符合规范标准,就可以通用。在Java中,接口可以看成是:多个类的公共规…

Spring核心配置步骤-完全基于XML的配置

Spring框架的核心配置涉及多个方面,包括依赖注入(DI)、面向切面编程(AOP)等。以下是一般情况下配置Spring应用程序的核心步骤: 1. **引入Spring依赖:** 在项目的构建工具(如Maven、…

23 WEB漏洞-文件上传之解析漏洞编辑器安全

目录 几种中间件解析漏洞简要演示几种常见WEB编辑器简要演示几种常见CMS文件上传简要演示贴近实际应用下的以上知识点演示 各个WEB编辑器安全讲解 https://navisec.it/编辑器漏洞手册/ 各个CMS文件上传简要讲解 wordpress,phpcms, 几种中间件解析漏洞简…

JDK 新版本中都有哪些新特性?

面试回答 JDK 8 推出了 Lambda 表达式、Stream、Optional、新的日期 API 等 JDK 9 中推出了模块化 JDK 10 中推出了本地变量类型推断 JDK 12 中增加了 switch 表达式 JDK 13 中增加了 text block JDK 14 中增加了 Records JDK 15 中增加了封闭类 JDK 17 中扩展了 switch…

探索未知世界:桌面端3D GIS引领地理信息新时代

近年来,桌面端的三维地理信息系统(3D GIS)在地理信息领域迎来了显著的发展,为我们带来了更深入、更丰富的地理空间认知和数据分析体验。从城市规划到环境保护,从资源管理到应急响应,桌面端的3D GIS正逐渐成…

Mysql定时备份事件

创建了一个名为backup_database的定时任务,每天自动在当前时间的后一天开始执行。备份数据库的代码使用mysqldump命令将数据库导出为sql文件保存在指定的备份目录中。 需要注意的是,上述代码中的用户名 (username)、密码 (password)、主机名 (hostname) …

Java程序设计——在一个给定的字符串中查找并解析姓名、出生日期、个人网站、身高和体重信息,并输出相应的结果

package ch4;public class FindMess {public static void main(String[] args) {String mess "姓名:张三 出生时间:1989.10.16。个人网站:http://www.zhang.com。身高:185 cm,体重:72 kg";int index (int)(mess.indexOf(":")); //mess调用indexOf(Stri…

如何解决跨域问题

跨域问题是指在浏览器中发起跨域请求时,由于浏览器的同源策略限制,导致请求被拒绝或无法正常发送和接收数据。同源策略要求两个页面具有相同的协议、域名和端口号,否则就会出现跨域问题。 为了解决跨域问题,可以采取以下方法之一…

git Update failed cannot lock ref

报错详情 解决方案 百度了很多方案,过滤出了有效方案 去该项目下的.git文件里找到报错文件,本例中即为:.git/refs/tags/pre-RELEASE-PRE-20230817-03 删除该文件,重新pull,pull成功问题解决

Java并发工具类

JDK并发包中常用并发工具类: CountDownLatch、CyclicBarrier和Semaphore工具类提供了一种并发流程控制的手段; Exchanger工具类则提供了在线程间交换数据的一种手段。 等待多线程完成的CountDownLatch CountDownLatch允许一个或多个线程等待其他线程完成…

Electron学习3 使用serialport操作串口

Electron学习3 使用serialport操作串口 一、准备工作二、 SerialPort 介绍1. 核心软件包(1) serialport(2) serialport/stream(3) serialport/bindings-cpp(4) serialport/binding-mock(5) serialport/bindings-interface 2. 解析器包3. 命令行工具 三、创建一个demo程序1. 创建…

NOI2015D. 荷马史诗

荷马史诗 题目描述 追逐影子的人,自己就是影子。 ——荷马 Allison 最近迷上了文学。她喜欢在一个慵懒的午后,细细地品上一杯卡布奇诺,静静地阅读她爱不释手的《荷马史诗》。但是由《奥德赛》和《伊利亚特》组成的鸿篇巨制《荷马史诗》实在是…

Dapper

介绍 dapper是一款轻量级的ORM Dapper 被称为 ORM 之王。 以下是 Dapper 的主要功能: 速度快,性能快。 更少的代码行。 对象映射器。 静态对象绑定。 动态对象绑定。 轻松处理 SQL 查询。 易于处理存储过程。 直接对 IDBConnection 类进行操作&#xf…