Python 设计模式(第2版) -- 第一部分(创建型模式)

Python 设计模式(第2版)

设计模式的主要特点如下所示:

  • 它们是语言无关的,可以用多种语言实现。
  • 它们是动态的,随时会有新的模式引入。
  • 它们可以进行定制,因此对开发人员非常有用。

设计模式的优点如下所示:

  • 它们可以在多个项目中重复使用。
  • 问题可以在架构级别得到解决。
  • 它们都经过了时间的验证和良好的证明,是开发人员和架构师的宝贵经验。
  • 它们具有可靠性和依赖性。

Python是一种动态语言。Python 的动态特性如下所示:

  • 类型或类是运行时对象。
  • 变量可以根据赋值来确定类型,并且类型可以在运行时改变。
  • 动态语言在类限制方面具有更大的灵活性。
  • 例如,在 Python 中,多态性是该语言所固有的,并没有诸如 private 和 protected 之类的关键字,因为默认情况下一切都是公共的。
  • 可以使用动态语言轻松实现设计模式的用例。

设计模式是由 GoF(Gang of Four)首先提出的,根据他们的观点,设计模式就是解决特定问题的解决方案。在他们的设计模式书中讲到了 23 种设计模式,并将它们分为三大类:

  • 创建型模式。
  • 结构型模式。
  • 行为型模式。

以下是创建型模式的性质:

  • 它们的运行机制基于对象的创建方式。
  • 它们将对象创建的细节隔离开来。
  • 代码与所创建的对象的类型无关。

以下是结构型模式的性质:

  • 它们致力于设计出能够通过组合获得更强大功能的对象和类的结构。
  • 重点是简化结构并识别类和对象之间的关系。
  • 它们主要关注类的继承和组合。

行为型模式具有下列性质:

  • 它们关注对象之间的交互以及对象的响应性。
  • 对象应该能够交互,同时仍然保持松散耦合。

首先来看看创建型设计模式有哪些。

单例模式

单例模式提供了这样一个机制,即确保类有且只有一个特定类型的对象,并提供全局访问点。因此,单例模式通常用于下列情形,例如日志记录或数据库操作、打印机后台处理程序,以及其他程序——该程序运行过程中只能生成一个实例,以避免对同一资源产生相互冲突的请求。

下面是基于 Python v3.5 的单例模式实现代码,它主要完成了两件事情。

1.只允许Singleton类生成一个实例。2.如果已经有一个实例了,我们会重复提供同一个对象。

class Singleton(object):def __new__(cls):if not hasattr(cls, 'instance'):cls.instance = super(Singleton, cls).__new__(cls)return cls.instances = Singleton()
print("Object created", s)s1 = Singleton()
print("Object created", s1)

单例模式的用例之一就是懒汉式实例化。懒汉式实例化能够确保在实际需要时才创建对象。所以,懒汉式实例化是一种节约资源并仅在需要时才创建它们的方式。

class Singleton:__instance = Nonedef __init__(self):if not Singleton.__instance:print(" __init__ method called..")else:print("Instance already created:", self.getInstance())@classmethoddef getInstance(cls):if not cls.__instance:cls.__instance = Singleton()return cls.__instances = Singleton() ## class initialized, but object not created
print("Object created", Singleton.getInstance()) # Object gets createdhere
s1 = Singleton() ## instance already created

作为一个实际的用例,我们将通过一个数据库应用程序来展示单例的应用。这里不妨以需要对数据库进行多种读取和写入操作的云服务为例进行讲解。

import sqlite3class MetaSingleton(type):_instances = {}def __call__(cls, *args, **kwargs):if cls not in cls._instances:cls._instances[cls] = super(MetaSingleton, cls). __call__(*args, **kwargs)return cls._instances[cls]class Database(metaclass=MetaSingleton):connection = Nonedef connect(self):if self.connection is None:self.connection = sqlite3.connect("db.sqlite3")self.cursorobj = self.connection.cursor()return self.cursorobjdb1 = Database().connect()
db2 = Database().connect()print ("Database Objects DB1", db1)
print ("Database Objects DB2", db2)

考虑另一种情况,即为基础设施提供运行状况监控服务(就像 Nagios 所作的那样)。首先创建了 HealthCheck 类,它作为单例实现。还要维护一个被监控的服务器列表。当一个服务器从这个列表中删除时,监控软件应该觉察到这一情况,并从被监控的服务器列表中将其删除。

class HealthCheck:_instance = Nonedef __new__(cls, *args, **kwargs):if not HealthCheck._instance:HealthCheck._instance = super(HealthCheck, cls). __new__(cls, *args, **kwargs)return HealthCheck._instancedef __init__(self):self._servers = []def addServer(self):self._servers.append("Server 1")self._servers.append("Server 2")self._servers.append("Server 3")self._servers.append("Server 4")def changeServer(self):self._servers.pop()self._servers.append("Server 5")hc1 = HealthCheck()
hc2 = HealthCheck()hc1.addServer()
print("Schedule health check for servers (1)..")
for i in range(4):print("Checking ", hc1._servers[i])hc2.changeServer()
print("Schedule health check for servers (2)..")
for i in range(4):print("Checking ", hc2._servers[i])

单例模式的缺点:

  • 全局变量可能在某处已经被误改,但是开发人员仍然认为它们没有发生变化,而该变量还在应用程序的其他位置被使用。
  • 可能会对同一对象创建多个引用。由于单例只创建一个对象,因此这种情况下会对同一个对象创建多个引用。
  • 所有依赖于全局变量的类都会由于一个类的改变而紧密耦合为全局数据,从而可能在无意中影响另一个类。

工厂模式 – 建立创建对象的工厂

工厂具有下列优点:

  • 松耦合,即对象的创建可以独立于类的实现。
  • 客户端无需了解创建对象的类,但是照样可以使用它来创建对象。它只需要知道需要传递的接口、方法和参数,就能够创建所需类型的对象了。这简化了客户端的实现。
  • 可以轻松地在工厂中添加其他类来创建其他类型的对象,而这无需更改客户端代码。最简单的情况下,客户端只需要传递另一个参数就可以了。
  • 工厂还可以重用现有对象。但是,如果客户端直接创建对象的话,总是创建一个新对象。

工厂模式有 3 种变体,如下所示:

  • 简单工厂模式:允许接口创建对象,但不会暴露对象的创建逻辑。
  • 工厂方法模式:允许接口创建对象,但使用哪个类来创建对象,则是交由子类决定的。
  • 抽象工厂模式:抽象工厂是一个能够创建一系列相关的对象而无需指定/公开其具体类的接口。该模式能够提供其他工厂的对象,在其内部创建其他对象。

1.简单工厂模式

现在,让我们借助 Python v3.5 代码示例来进一步理解简单工厂模式。首先将创建一个名为 Animal 的抽象产品。Animal 是一个抽象的基类(ABCMeta 是 Python 的特殊元类,用来生成类 Abstract),它带有方法 do_say()。我们利用 Animal 接口创建了两种产品(Cat 和 Dog),并实现了 do_say() 方法来提供这些动物的相应的叫声。ForestFactory 是一个带有 make_sound() 方法的工厂。

from abc import ABCMeta, abstractmethodclass Animal(metaclass = ABCMeta):@abstractmethoddef do_say(self):passclass Dog(Animal):def do_say(self):print("Bhow Bhow! ! ")class Cat(Animal):def do_say(self):print("Meow Meow! ! ")## forest factory defined
class ForestFactory(object):def make_sound(self, object_type):return eval(object_type)().do_say()## client code
if __name__ == '__main__':ff = ForestFactory()animal = input("Which animal should make_sound Dog or Cat? ")ff.make_sound(animal)

2.工厂方法模式

通过一个现实世界的场景来理解工厂方法的实现,假设想在不同类型的社交网络(例如 LinkedIn、Facebook 等)上为个人或公司建立简介。那么,每个简介都有某些特定的组成章节。在 LinkedIn 的简介中,有一个章节是关于个人申请的专利或出版作品的。在 Facebook 上,你将在相册中看到最近度假地点的照片区。此外,在这两个简介中,都有一个个人信息的区。

首先定义接口 Product,将创建一个 Section 抽象类来定义一个区是关于哪方面内容的,让它尽量保持简单,同时还提供一个抽象方法 describe()。然后,会创建多个 ConcreteProduct、PersonalSection、AlbumSection、PatentSection 和 PublicationSection 类。创建了一个名为 Profile 的抽象类Creator。Profile [Creator]抽象类提供了一个工厂方法,即 createProfile()。createProfile() 方法应该由 ConcreteClass 实现,来实际创建带有适当区的简介。创建了两个 ConcreteCreator 类,即 linkedin 和 facebook。每个类都实现 createProfile() 抽象方法。

from abc import ABCMeta, abstractmethodclass Section(metaclass=ABCMeta):@abstractmethoddef describe(self):passclass PersonalSection(Section):def describe(self):print("Personal Section")class AlbumSection(Section):def describe(self):print("Album Section")class PatentSection(Section):def describe(self):print("Patent Section")class PublicationSection(Section):def describe(self):print("Publication Section")class Profile(metaclass=ABCMeta):def __init__(self):self.sections = []self.createProfile()@abstractmethoddef createProfile(self):passdef getSections(self):return self.sectionsdef addSections(self, section):self.sections.append(section)class linkedin(Profile):def createProfile(self):self.addSections(PersonalSection())self.addSections(PatentSection())self.addSections(PublicationSection())class facebook(Profile):def createProfile(self):self.addSections(PersonalSection())self.addSections(AlbumSection())if __name__ == '__main__':profile_type = input("Which Profile you'd like to create? [LinkedIn or FaceBook]")profile = eval(profile_type.lower())()print("Creating Profile..", type(profile). __name__)print("Profile has sections --", profile.getSections())

工厂方法模式的优点:

  • 它具有更大的灵活性,使得代码更加通用,因为它不是单纯地实例化某个类。这样,实现哪些类取决于接口(Product),而不是 ConcreteProduct类。
  • 它们是松耦合的,因为创建对象的代码与使用它的代码是分开的。客户端完全不需要关心要传递哪些参数以及需要实例化哪些类。由于添加新类更加容易,所以降低了维护成本。

3.抽象工厂模式

抽象工厂模式的主要目的是提供一个接口来创建一系列相关对象,而无需指定具体的类。

如何实现抽象工厂模式,讨论披萨饼店提供多种披萨饼订购的场景。加入开办了一家披萨店,供应美味的印式和美式披萨饼。为此,首先创建一个抽象基类 —— PizzaFactory。PizzaFactory 类有两个抽象方法即 createVegPizza() 和 createNonVegPizza(),它们需要通过 ConcreteFactory 实现。在这个例子中,我们创造了两个具体的工厂,分别名为 IndianPizzaFactory 和 USPizzaFactory。然后,将创建两个抽象类:VegPizza 和 NonVegPizza。再创建 DeluxVeggiePizza 和 MexicanVegPizza,并实现 prepare() 方法。接下来,我们来定义 ChickenPizza 和 HamPizza,并实现 server() 方法。

from abc import ABCMeta, abstractmethodclass PizzaFactory(metaclass=ABCMeta):@abstractmethoddef createVegPizza(self):pass@abstractmethoddef createNonVegPizza(self):passclass IndianPizzaFactory(PizzaFactory):def createVegPizza(self):return DeluxVeggiePizza()def createNonVegPizza(self):return ChickenPizza()class USPizzaFactory(PizzaFactory):def createVegPizza(self):return MexicanVegPizza()def createNonVegPizza(self):return HamPizza()class VegPizza(metaclass=ABCMeta):@abstractmethoddef prepare(self, VegPizza):passclass NonVegPizza(metaclass=ABCMeta):@abstractmethoddef serve(self, VegPizza):passclass DeluxVeggiePizza(VegPizza):def prepare(self):print("Prepare ", type(self). __name__)class ChickenPizza(NonVegPizza):def serve(self, VegPizza):print(type(self). __name__, " is served with Chicken on ", type(VegPizza). __name__)class MexicanVegPizza(VegPizza):def prepare(self):print("Prepare ", type(self). __name__)class HamPizza(NonVegPizza):def serve(self, VegPizza):print(type(self). __name__, " is served with Ham on ", type(VegPizza). __name__)class PizzaStore:def __init__(self):passdef makePizzas(self):for factory in [IndianPizzaFactory(), USPizzaFactory()]:self.factory = factoryself.NonVegPizza = self.factory.createNonVegPizza()self.VegPizza = self.factory.createVegPizza()self.VegPizza.prepare()self.NonVegPizza.serve(self.VegPizza)pizza = PizzaStore()
pizza.makePizzas()

工厂方法和抽象工厂方法比较

工厂方法抽象工厂方法
向客户端开放了一个创建对象的方法包含一个或多个工厂方法来创建一个系列的相关对象
使用继承和子类决定要创建哪个对象使用组合将创建对象的任务委托给其他类
工厂方法用于创建一个产品用于创建相关产品的系列

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

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

相关文章

java架构设计-COLA

参考:https://github.com/alibaba/COLA 架构 要素:组成架构的重要元素 结构:要素直接的关系 意义:定义良好的结构,治理应用复杂度,降低系统熵值,改善混乱状态 创建COLA应用: mvn …

Git的3个主要区域

一般来说,日常使用只要记住下图6个命令,就可以了。但是熟练使用,恐怕要记住60~100个命令。 下面是我整理的常用 Git 命令清单。几个专用名词的译名如下。 Workspace:工作区 Index / Stage:暂存区 Reposito…

git pull的使用方法

git pull 是 Git 中的一个常用命令&#xff0c;它结合了 fetch 和 merge 两个操作&#xff0c;用于从远程仓库拉取最新的更改&#xff0c;并将其合并到本地仓库的当前分支中。这个命令可以帮助你保持本地代码与远程仓库同步。 ### 基本语法 bash git pull [options] [<rem…

你的企业真的适合做私域吗?

现在&#xff0c;都在提倡企业做私域&#xff0c;可是所有的企业都适合做私域吗&#xff1f;看看市场上成功的案例&#xff0c;显然&#xff0c;并不是所有企业都适合做私域&#xff0c;所以&#xff0c;做私域之前&#xff0c;企业也应该充分的分析&#xff0c;自己的优势是什…

spark常见问题

写文章只是为了学习总结或者工作内容备忘&#xff0c;不保证及时性和准确性&#xff0c;看到的权当个参考哈&#xff01; 1. 执行Broadcast大表时&#xff0c;等待超时异常&#xff08;awaitResult&#xff09; 现象&#xff1a;org.apache.spark.SparkException: Exception…

玩转OurBMC第八期:OpenBMC webui之通信交互

栏目介绍&#xff1a;“玩转OurBMC”是OurBMC社区开创的知识分享类栏目&#xff0c;主要聚焦于社区和BMC全栈技术相关基础知识的分享&#xff0c;全方位涵盖了从理论原理到实践操作的知识传递。OurBMC社区将通过“玩转OurBMC”栏目&#xff0c;帮助开发者们深入了解到社区文化、…

【网络】序列化和反序列化

一、序列化和反序列化 序列化和反序列化是计算机中用于数据存储和传输的重要概念。 1.序列化 &#xff08;Serialization&#xff09; 是将数据结构或对象转换成一种可存储或可传输格式的过程。在序列化后&#xff0c;数据可以被写入文件、发送到网络或存储在数据库中&…

UITableView之显示单组数据Demo

需求 UITableView实现显示单组数据。尝试设置不同行高度不同。 效果&#xff1a; 数据展示 实现 与之前分组显示数据的区别在于懒加载的数据模型不同。 &#xff08;1&#xff09;声明数据模型类 类的属性一定要和plist中数据的字段保持一致 interface CZhero : NSObject /…

go 语言爬虫库goQuery 的详细使用(知乎日报详情页解析示例)

上一篇《uniapp小程序开发 | 从零实现一款影视类app 》实现了影视小程序的前端和后台接口&#xff0c;虽然包含了大多数小程序应有的知识&#xff0c;但基本还只是涉及网络接口和vue页面的设计。这里介绍下零一个有趣的练手项目&#xff0c;知乎日报。涉及详情页面的html解析&a…

CDA二级(Level II)数据分析师——考试内容梳理四

定额抽样不属于概率抽样 、类型抽样就是分群抽样&#xff1b; 在假设检验中&#xff0c;两类错误的概率相加后不等于1&#xff0c; 在样本量增大的条件下&#xff0c;两类错误的概率可以同时减小&#xff0c; 通常控制第一类错误的概率 &#xff1b;&#xff08;去真&#xff…

线性稳压器LDO的基础知识

一、什么是线性稳压器? 线性稳压器的工作原理是&#xff1a;采用一个压控电流源以强制在稳压器输出端上产生一个固定电压。控制电路连续监视&#xff08;检测&#xff09;输出电压&#xff0c;并调节电流源&#xff08;根据负载的需求&#xff09;以把输出电压保持在期望的数值…

【Spine学习12】之 事件帧

1、新建事件帧&#xff1a; 2、选择第8s的攻击帧&#xff0c;点击第一步新建的attack事件帧前面的钥匙 这样每次动作到8s的时候会自动跳出事件帧提示 这个文字实际动画不会显示 事件是动画过程中所发生情况的触发器。 给程序员识别的

分享:2024年(第12届)“泰迪杯”数据挖掘挑战赛省级奖项获奖名单公示

本次竞赛有评选省奖的省份有广东省、广西壮族自治区、河北省、湖北省。各省奖项依据“泰迪杯”全国评审专家组统一评阅的最终成绩区分省份后从高到低依序按比例产生。 广东省 省级奖项获奖名单公示 奖项设置&#xff1a; 一等奖&#xff1a;约占该省份队伍总数的5%&#xff0…

Android开发系列(四)Jetpack Compose之Button

在Jetpack Compose中&#xff0c;Button是一个常用的用户界面组件&#xff0c;用于执行某些操作或触发某些事件。Button控件是可触摸的&#xff0c;并且通常会显示一个文本或图标来表示其功能。 要在Jetpack Compose中创建一个Button&#xff0c;可以使用Button()函数&#xf…

Shell中执行.sh文件的常见方式

在Shell中执行.sh文件有几种常见的方式&#xff0c;具体取决于你希望如何执行这个脚本文件。以下是一些常用的方法&#xff1a; 直接运行&#xff1a; ./script.sh 这是最简单的方式。在当前Shell会话中执行脚本文件。 使用bash命令执行&#xff1a; bash script.sh 明确使用b…

shell学习记录

shell简介 参考博文1 参考博文2——shell语法及应用 参考博文3——vi的使用 在linux中有很多类型的shell&#xff0c;不同的shell具备不同的功能&#xff0c;shell还决定了脚本中函数的语法&#xff0c;Linux中默认的shell是 / b in/ b a s h &#xff0c;流行的shell有as…

spring @Order注解

Order 注解的作用&#xff1a;Order 注解通常用于同一类型的多个处理器之间的排序 Order 注解的确是用来控制同一类型的多个处理器、切面或过滤器的执行顺序。为了更好地理解这一点&#xff0c;我们可以通过一些具体的例子来说明。 示例 1: 多个异常处理器 假设你有多个异常…

pgsql给单独数据库制定账号权限

登录到PostgreSQL: 使用psql或其他PostgreSQL客户端&#xff0c;以具有足够权限的账号&#xff08;如postgres或superuser&#xff09;登录。 2. 创建新账号: sql复制代码 CREATE USER new_user WITH PASSWORD your_secure_password; 注意&#xff1a;将your_secure_passwor…

B-splines曲线的绘制(Matlab)

虽然在这个链接三次 Bspline(B样条曲线) NURBS曲线的绘制 matlab_三次b样条曲线的绘制-CSDN博客中我们介绍了NURBS曲线&#xff0c;然而有时候我们通过B-spline曲线也能够解决问题。B-spline曲线作为NURBS曲线的一种特例&#xff0c;这里给出均匀B-spline曲线的表达式&#xff…

【Arc gis】Arc gis出现ERROR 999999问题的解决办法

地址&#xff1a;ArcGIS中ERROR 999999报错Configuration RasterCommander ImageServer can not be started解决_投影栅格失败error999999-CSDN博客