参考博客 https://baijiahao.baidu.com/s?id=1758410771062793648&wfr=spider&for=pc
设计模式(Design pattern)
"""
对软件开发中【普遍存在(反复出现)的问题】,而提出的【解决方案】。每一个设计模式系统地命名、解释和评价了面向对象系统中一个重要和重复出现的设计通过设计模式可以帮助我们增强代码的可重用性、可扩充性、 可维护性、灵活性好。
我们使用设计模式最终的目的是实现代码的高内聚和低耦合。一般需要写底层模块或者框架的时候,就需要大量使用到设计模式了
"""# 3、行为型模式类和对象如何交互,及划分责任和算法。
"""
策略模式:定义一系列算法,把他们封装起来,并且使它们可以相互替换。
模板模式:定义一个算法结构,而将一些步骤延迟到子类实现。
命令模式:将命令请求封装为一个对象,使得可以用不同的请求来进行参数化。
迭代器模式:一种遍历访问聚合对象中各个元素的方法,不暴露该对象的内部结构。
观察者模式:对象间的一对多的依赖关系。
中介者模式:用一个中介对象来封装一系列的对象交互。
备忘录模式:在不破坏封装的前提下,保持对象的内部状态。
解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器。
状态模式:允许一个对象在其对象内部状态改变时改变它的行为。
责任链模式:将请求的发送者和接收者解耦,使的多个对象都有处理这个请求的机会。
访问者模式:不改变数据结构的前提下,增加作用于一组对象元素的新功能。
"""
创建型模式(5种)
【创建型模式主要聚焦于怎么样去创建一个对象,用于解耦对象的实例化过程。】单例(Singleton)模式:某个类只能有一个实例,提供一个全局的访问点。工厂(Factory)模式:一个工厂类根据传入的参量,决定创建出哪一种产品类的实例。抽象工厂(AbstractFactory)模式:创建相关或依赖对象的家族,而无需明确指定具体类。建造者(Builder)模式:封装一个复杂对象的创建过程,并可以按步骤构造。原型(Prototype)模式:通过复制现有的实例来创建新的实例。
结构型模式(7种)
【结构型模式主要聚焦于多个类之间应该组成什么结构,才能比较合理的实现功能】适配器(Adapter Pattern)模式:将一个类的方法接口转换成客户希望的另一个接口。桥接(Bridge)模式:将抽象部分和它的实现部分分离,使它们都可以独立的变化。组合(Composite)模式:将对象组合成树形结构以表示“部分-整体”的层次结构。外观(Facade)模式:对外提供一个统一的方法,来访问子系统中的一群接口。代理(Proxy Pattern)模式:为其它对象提供一个代理以便控制这个对象的访问。装饰器(Decorator)模式:动态的给对象添加新的功能。享元(Flyweight)模式:通过共享技术来有效的支持大量细粒度的对象。
行为型模式(11种)
【行为型模式主要聚焦于类的行为或类的方法】责任链(Chain of Responsibility)模式:将请求的发送者和接收者解耦,使的多个对象都有处理这个请求的机会。观察者(Observer)模式:对象间的一对多的依赖关系。当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。策略(Strategy)模式:定义一系列算法,把他们封装起来,并且使它们可以相互替换。模板方法(Template Method)模式:定义一个算法结构,而将一些步骤延迟到子类实现。命令(Command)模式:将命令请求封装为一个对象,使得可以用不同的请求来进行参数化。【主要用来做桌面程序的】迭代器模式(Iterator):一种遍历访问聚合对象中各个元素的方法,不暴露该对象的内部结构。中介者(Mediator)模式:用一个中介对象来封装一系列的对象交互。备忘录(Memento Pattern)模式:在不破坏封装的前提下,保持对象的内部状态。状态(State Pattern)模式:允许一个对象在其对象内部状态改变时改变它的行为。访问者(Visitor Pattern)模式:不改变数据结构的前提下,增加作用于一组对象元素的新功能。
面向对象设计模式的七种原则
通常被称为“SOLID原则” 这七个原则分别是:#1 单一职责原则(Single Responsibility Principle,SRP):
一个类只负责一个职责或只干一件事。
这个原则强调的是高内聚、低耦合,可以降低类的复杂度,提高代码的可读性、可维护性和可重用性。#2 开闭原则(Open-Closed Principle,OCP):
一个软件实体如类、模块和函数,应该对扩展开放,对修改关闭。
即一个已经正常运行的软件实体,【应尽量在不修改原有代码的情况下进行扩展。】
这个原则强调的是代码的可维护性和可扩展性,通过抽象化来避免修改已有代码的风险,从而降低软件维护的成本。#3 里氏替换原则(Liskov Substitution Principle,LSP):
子类应该可以替换其父类,并且不会影响程序的正确性。
【就是子类里面,重写父类里面方法,重写的方法名称,参数,返回值 都要与父类的方法一样】,
这样别人在调用该子类的方法的时候,就可以和调用它的父类的方法,一样操作了
这个原则强调的是面向对象的继承和多态特性,
通过【保证子类的行为和父类一致】,从而提高代码的可维护性和可扩展性。#4 接口隔离原则(Interface Segregation Principle,ISP):
【一个类不应该依赖它不需要的接口】,应该使用多个专门的接口,而不是使用一个单一的总接口
比如我们定义了一个抽象类Animal里面定义了3个抽象方法 比如walk、swim、fly
那么每一个继承该抽象类的子类,都要去实现这3个方法,但是如果子类是一个Dog 那么就不需要有fly方法
但是因为继承了Animal抽象类,必须要写fly方法,不合理,怎么解决?
把这一个Animal抽象类拆分3个抽象类 LandAnimal、WaterAnimal、SkyAnimal
每个抽象类里面定义一个抽象方法,这样子类就可以通过多继承来实现,要写对应的几个方法了
这个原则强调的是接口设计的合理性,
避免不必要的接口导致类之间的耦合性过高,从而提高代码的灵活性和可维护性。#5 依赖倒置原则(Dependency Inversion Principle,DIP):
高层模块不应该依赖于底层模块,二者都应该依赖二者的抽象,或者说细节(子类)应该依赖抽象(父类)
换言之 【要针对接口编程,而不是针对实现编程】
高层模块不应该依赖于底层模块,如果底层模块的修改变动,导致已经写好的高层模块代码也修改,那就不太好了
所以写程序时,最好先定义好接口,接口需要表明高层代码需要调用底层的哪些函数
这个原则强调的是代码的可扩展性和可维护性,
【通过抽象化来减少组件之间的耦合性】,从而使得代码更加灵活、易于维护和扩展。#--------------------下面两个是扩展的原则--------------------##6 迪米特法则(Law of Demeter,LoD):
也叫最少知识原则Least Knowledge Principle,LKP
一个对象应当对其他对象要尽可能少的了解,不需要了解的内容尽量不要去了解。
这个原则强调的是组件之间的松耦合,通过减少组件之间的依赖关系,提高代码的可维护性和可重用性。#7 组合/聚合复用原则(Composite/Aggregate Reuse Principle,CARP):
尽量使用组合或聚合关系,而不是继承关系来达到代码复用的目的。
这个原则强调的是通过组合和聚合的方式来实现代码复用,
避免继承带来的一些问题,如父类和子类之间的强耦合性,从而提高代码的灵活性和可维护性。
我们在选择创建对象的设计模式时,
一般先看简单的模式能不能满足需求,能满足就用简单的
当发现设计需要更大的灵活性时,
再考虑使用 抽象工厂 或者 建造者模式 复杂的设计模式来实现
1 工厂模式(Factory)
# 定义一个创建对象的接口,让子类决定实例化哪一个类。"""工厂模式最关键的特性就是: 客户端不用去关心类怎么实现的,只要去关心工厂类就行了一个工厂类负责创建多个不同类型的对象。
该工厂类通常包含一个公共的静态方法,该方法接受一个参数,用于指示要创建的对象类型,
然后根据该参数创建相应的对象并返回给客户端。"""
1.1 简单工厂模式
"""不直接向用户暴露对象创建的实现细节,而是通过一个工厂类来决定哪一种创建产品类的实例
简单工厂模式可以隐藏对象创建的复杂性,使高层使用该类的代码,更加简洁和易于维护。优点:隐藏了对象创建的实现细节,高层不需要修改代码,当该类代码发生改变的时候
缺点:#1 违反了单一职责原则,因为工厂类不仅负责对象的创建,还负责判断要创建哪个对象的逻辑。比如产品类的代码,因为需求,需要在实例化的时候再传一个参数,那么这个时候,就必须要改该工厂类的对象的创建逻辑处的代码了#2 违反了开闭原则,如果需要添加新的产品类时,则必须修改工厂类的代码,添加如何使用该产品类的逻辑"""# 下面是实现简单工厂模式的示范代码class ProductA:def __init__(self,flag=Flase):self.flag = flagdef operation(self):if not self.flag:return "AAAAAAAAAA"else:return "hahaha"class ProductB:def operation(self):return "BBBBBBBBBB"# 把多种产品对象的创建逻辑,都集中在一个类里面了
class SimpleFactory:@staticmethoddef create_product(product_type):if product_type == "A":# 这里可以写一些再创建对象前的逻辑,比如构造需要的各种参数 #return ProductA()elif product_type == "B":# 这里可以写一些再创建对象前的逻辑,比如构造需要的各种参数 #return ProductB()elif product_type == "other":# 这里可以写一些再创建对象前的逻辑,比如构造需要的各种参数 #return ProductA(flag=True)else:raise ValueError("Invalid product type")if __name__ == "__main__":product_a = SimpleFactory.create_product("A")product_b = SimpleFactory.create_product("B")product_other = SimpleFactory.create_product("other")print(product_a.operation()) # 输出:AAAAAAAAAAprint(product_b.operation()) # 输出:BBBBBBBBBBprint(product_other.operation()) # 输出:hahaha"""为什么要有工厂类,直接用对应的类去实例化不更方便吗?假设ProductA与ProductB类在实例化的时候,需要很多参数,
但是这些参数,可能调用该类的人完全不需要知道,怎么样让调用该类的人,不传这些参数也能正常拿到对象了?
把类的实例化代码,封装到工厂类里面,在工厂类里面写如果拿到这些参数,并在实例化的时候传这些参数
最后在工厂类里面,提供一个对外的函数,
用户只要调用该工厂类的函数,传关键的一些参数,就可以获得对应类的对象,这样对用户就比较友好了
用户不需要知道类的实例化的具体逻辑,就能简单的实现,根据不同的参数,创建不同的类的对象了
以及根据不同的参数,让同一个类,创建不同功能的对象了"""
1.2 工厂方法模式
"""定义了一个创建对象的接口(抽象工厂类),让子工厂类决定要实例化哪一个产品类。工厂模式的优点:
每一个具体产品类都对应一个具体工厂类
可以轻松地添加新的子工厂类与新的产品类来创建新的产品对象,而无需更改现有的代码。
隐藏了对象的创建细节缺点:
每增加一个具体产品类,就必须增加一个相应的具体工厂类"""# 下面是实现工厂方法模式的示范代码class ProductA:def __init__(self,flag=Flase):self.flag = flagdef operation(self):if not self.flag:return "AAAAAAAAAA"else:return "hahaha"class ProductB:def operation(self):return "BBBBBBBBBB"# 定义抽象工厂类约束子工厂类的行为
class AbsFactory(ABC):@abstractmethoddef create_product(product_type):pass# 定义多个子工厂类,每个子工厂类负责一个产品类的实例化
class AFactory(AbsFactory):def create_product(self):# 这里可以写一些再创建对象前的逻辑,比如构造需要的各种参数 #return ProductA()class BFactory(AbsFactory):@staticmethoddef create_product(self):# 这里可以写一些再创建对象前的逻辑,比如构造需要的各种参数 #return ProductB()class OtherFactory(AbsFactory):@staticmethoddef create_product(self):# 这里可以写一些再创建对象前的逻辑,比如构造需要的各种参数 #return ProductA(flag=True)if __name__ == "__main__":product_a = SimpleFactory.create_product("A")product_b = SimpleFactory.create_product("B")product_other = SimpleFactory.create_product("other")print(product_a.operation()) # 输出:AAAAAAAAAAprint(product_b.operation()) # 输出:BBBBBBBBBBprint(product_other.operation()) # 输出:hahaha# 简单点理解 就是通过工厂类的对象,去调用不同的类,去生成不同的对象
2 抽象工厂(Abstract Factory)模式
【定义一个接口,让工厂子类来创建一系列相关或相互依赖的对象】
相比工厂方法模式,抽象工厂模式中的每一个具体工厂类,都生产一套产品类对象
"""
优点:将客户端与类的具体实现相分离每个工厂创建一套完整的产品系列,使得易于交换产品系列
缺点: 抽象工厂类一旦删除或添加新的抽象方法,每一个继承它的子类都要改所以一般会再重新定义一个抽象工厂类,单独给某一个特殊的具体工厂类用结构比较复杂
"""from abc import ABC, abstractmethod#--------------------------------#
# 抽象产品类
class PhoneShell(ABC):@abstractmethoddef show_shell(self):passclass Cpu(ABC):@abstractmethoddef show_cpu(self):passclass Os(ABC):@abstractmethoddef show_os(self):pass
#--------------------------------## 具体产品类
class HuaweiShell(PhoneShell):def show_shell(self):print("华为手机壳")class XiaomiShell(PhoneShell):def show_shell(self):print("小米手机壳")class OppoShell(PhoneShell):def show_shell(self):print("Oppo手机壳")class Gaotong(Cpu):def show_cpu(self):print("高通cpu")class Lianfk(Cpu):def show_cpu(self):print("联发科cpu")class Android(Os):def show_os(self):print("Android系统")class Harmony(Os):def show_os(self):print("鸿蒙系统")#--------------------------------# # 抽象工厂类
class PhoneFactory(ABC):@abstractmethoddef make_shell(self):pass@abstractmethoddef make_cpu(self):pass@abstractmethoddef make_os(self):pass#--------------------------------## 具体工厂类
class HuaweiFactory(PhoneFactory):def make_shell(self):return HuaweiShell()def make_cpu(self):return Gaotong()def make_os(self):return Harmony()class XiaomiFactory(PhoneFactory):def make_shell(self):return XiaomiShell()def make_cpu(self):return Gaotong()def make_os(self):return Android()class OppoFactory(PhoneFactory):def make_shell(self):return OppoShell()def make_cpu(self):return Lianfk()def make_os(self):return Android()# 客户端-----------------------------------------------class Phone:def __init__(self, shell, cpu, os):self.shell = shellself.cpu = cpuself.os = osdef show_info(self):print("手机信息:")self.shell.show_shell()self.cpu.show_cpu()self.os.show_os()def make_phone(factory:PhoneFactory):shell_obj = factory.make_shell()cpu_obj = factory.make_cpu()os_obj = factory.make_os()return Phone(shell_obj, cpu_obj, os_obj)# 通过具体的工厂类,生成一系列产品类
xiaomi_obj = make_phone(XiaomiFactory())
xiaomi_obj.show_info()oppo_obj = make_phone(OppoFactory())
xiaomi_obj.show_info()
3 建造者(Builder)模式
【将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的复杂对象】"""
建造者模式与抽象工厂模式相识,也是用来创建复杂对象
建造者模式着重一步一步构造一个复杂对象,抽象工厂模式着重一次构建多个产品类对象出来优点:隐藏了产品的内部结构和装配过程将构造代码与表示代码分开可以对构造过程进行更精细的控制
"""from abc import ABC, abstractmethod#--------------------------------## 产品
class Player:def __init__(self, face=None, body=None, arm=None, leg=None):self.face = faceself.body = bodyself.arm = armself.leg = legdef __str__(self) -> str:return f"{self.face}, {self.body}, {self.arm}, {self.leg}"# 抽象建造者
class PlayerBuilder(ABC):@abstractmethoddef build_face(self):pass@abstractmethoddef build_body(self):pass@abstractmethoddef build_arm(self):pass@abstractmethoddef build_leg(self):pass# 具体建造者1
class GirlPlayerBuilder(PlayerBuilder):def __init__(self):self.player = Player()def build_face(self):self.player.face = "beautiful Face"def build_body(self):self.player.body = "beautiful Body"def build_arm(self):self.player.arm = "beautiful Arm"def build_leg(self):self.player.leg = "beautiful Leg"# 具体建造者2
class MonsterPlayerBuilder(PlayerBuilder):def __init__(self):self.player = Player()def build_face(self):self.player.face = "毛 Face"def build_body(self):self.player.body = "毛 Body"def build_arm(self):self.player.arm = "毛 Arm"def build_leg(self):self.player.leg = "毛 Leg"# 指挥者 控制组装顺序
class PlayerDirector:def build_player(self, builder)->Player:builder.build_body()builder.build_face()builder.build_arm()builder.build_leg()return builder.player# 客户端
builder_obj1 = GirlPlayerBuilder() # 建造者
director_obj = PlayerDirector()
# 设计者,导演,决定了建造者按照什么顺序,去组装生成玩家对象player_obj1 = director_obj.build_player(builder_obj1) # 生成玩家对象
print(player_obj1)builder_obj2 = MonsterPlayerBuilder()
player_obj2 = director_obj.build_player(builder_obj2)
print(player_obj2)
补充: 类工厂
# 类工厂
顾名思义就是创造类的工厂(函数),也就是函数的返回值是一个类。我们可以使用这个类生成实例。
而每一次执行函数都会得到一个"不同"(地址不同)的类,
我们可以用不同的变量去接收这些类,并使用这些个类完成实例化得到实例。# 因此类工厂最大的作用就是:
【可以不用在执行前(编码时)就确定好我的类需要有哪些属性,有哪些方法,】
【而是在执行过程中,根据执行结果,自动生成我们所需要的类】
这个类包含着我们需要的属性和方法(其实这里可以体会一下为什么Python是一个动态语言)。# 类工厂函数代码示范def create_class():"""类工厂返回类对象, 注意返回的是类, 并不是类实例化的对象"""class People(object):def __init__(self, name, age):self.name = nameself.age = agedef study(self):print(f"{self.name}正在学习")return PeoplePeople1 = create_class() # 创建People类
People2 = create_class() # 创建People类
# 这里是两个类, 类似于以下两个语句
# People1 = type("People", (object,), {...})
# People2 = type("People", (object,), {...})print(id(People1) == id(People2)) # False 这两个类的地址是不是一样的
print(People1) # <class '__main__.create_class.<locals>.People'>
print(People2) # <class '__main__.create_class.<locals>.People'>ppp1 = People1("小明",13)
ppp2 = People2("小华",14)
print(isinstance(ppp1, People1)) # true
print(isinstance(ppp2, People1)) # false"""
People1和People2指向的类对象地址是不一样的,
尽管是通过同一个函数得到的类,但实际上是两个不同的类,
虽然都叫People(这里的People是类的名字)。下面我们分别使用People1和People2创造了两个实例,
并使用isinstance来判断实例与类的关系。
我们发现,ppp2实例通过isinstance判断出与People1类是没有关系的。
这也进一步说明,通过类工厂函数创造出的多个类,类与类之间没有任何关系,
只不过是这些类,包含的功能一样罢了。
"""
"""
类工厂函数有什么用呢?
一般情况下使用类工厂函数,是为了【能根据传递的参数,来选择性的构建类。】
比如有个需求创建People类,Dog类,Car类,
每一个类都有自己的初始化属性,
比如People类需要name和age,Dog类需要name和color,Cat类需要brand等。
一种很容易想到的方式就是我直接创建三个类,需要用哪个就用哪个。我们能不能进行整合呢?
比如我先把这些信息配置到一个文件中,
并通过类工厂函数的参数,从文件中得到我们需要的初始化属性,并创建一个类对象返回呢?
看下面的代码:
"""import csv# 调用该类工程函数,参数传要生成类的类名,就会先去打开.csv配置文件
# 然后根据你传的类名,找到配置文件里面,该类名对应的所有属性名,放到params参数列表里
# 也就是说,如果调用该create_class函数传不一样的classname,就会得到不一样的params参数列表# 然后再定义一个Animal类,该Animal类里面定义的__init__函数写的有点优雅
# 先通过判断params参数列表 与 用户在实例化该类的时候,传的关键字参数的数量与名字 是否一致
# 来决定是否要按用户传的参数来实例化def create_class(classname):params = [] ## 用于记录需要由类初始化时,需要有那些属性with open("类配置.csv", "r", encoding="utf-8-sig") as f:for row in csv.reader(f): # 按行读取, row为列表, 以,分隔元素if row == [] or row[0] != classname: ## 如果不是属于该类的属性则跳过continueparams.append(row[1])class Animal(object):expected_param = params # 类变量def __init__(self, **kwargs):# 检查类配置文件里面指定类的属性 与 用户传的关键字参数是否一致if set(self.expected_param) != set(kwargs.keys()):raise ValueError(f"初始化属性与类不匹配, 需要有属性{self.expected_param}")# 用户传的关键字参数 的数量与名字都对了,执行添加属性的操作for k, v in kwargs.items():setattr(self, k, v)# 我们这里给用户添加属性与属性值 为什么不直接 self.属性名 = 属性值# 而要采用setattr这种方法,进行对象初始化?# 因为每种不一样的类的属性是不一样的,这里不能写死掉,才用setattr这种方法的def print_info(self):info_str = "对象具有以下属性: "# self.xxx也能访问类属性for key in self.expected_param: info_str += f"{key}={getattr(self, key)} "return info_strreturn Animal # 返回类
类的配置文件
"""
第一列表示类的名称,第二列表示对应类需要那些初始化属性。
需要注意的是由于我们编码时并不知道要创造哪个类,
因此我们在__init__中使用setattr(self,属性名,属性值)的方式创造属性。我们测试一下:
"""
Animal1 = create_class("People")
Animal2 = create_class("Car")# 实例化的时候必须要用关键字的方式传参,并且传的参数个数不能多,也不能少 要要配置文件一致
people_obj1 = Animal1(name="小明", age=14, gender="男") ## 用Animal1类创造对象
car_obj2 = Animal2(money=20000, color="红色", brand="宝马") ## 用Animal2类创造对象print(my_object1.print_info()) # 对象具有以下属性: name="小明" age=14 gender="男"
print(my_object2.print_info()) # 对象具有以下属性: money=20000 color="红色" brand="宝马""""
总结【
当我们需要在运行时,确认类的属性有哪些,
而不是在编码时就定义死,类有哪些属性时,类工厂的作用就显现出来了。】
"""
补充知识:abc模块使用
# 接口: 若干抽象方法的集合
# 作用: 限制类必须实现,接口指定的方法,并且参数名称与个数也要一致"""
abc模块 abstract class 当我们想要约束一个类必须要实现一个方法,并且这方法的参数名称与个数,也必须是我们规定好的
就是约束一个类里面必须要有一个规定好的方法,并且该方法的样子也是规定的样子,
至于方法里面的具体代码不用管
怎么办?"""#1 普通方法实现
class Shape:def area(self):raise NotImplementedErrorclass Rectangle(Shape):def __init__(self, width, height):self.width = widthself.height = height
# 假设Rectangle类里面不定义area函数,一旦类的实例调用area方法,就会报错
# 这种方法也约束了子类的行为,不定义area函数,一旦对象调用了父类的area方法,就会报错了#-------------------------------------------------------------#"""
Python中的 abc模块提供了抽象基类的支持,
抽象类是一种不能直接被实例化的类,它的主要作用是【定义接口和规范子类的行为。】, 它只是一个接口
ABC 是一个抽象基类,它的子类必须实现指定的抽象方法。
如果子类没有实现抽象方法,则在实例化子类对象时会抛出 TypeError 异常。abstractmethod 是一个装饰器,它用于指定一个抽象方法。
抽象方法是一个没有实现的方法,需要由子类去实现。
"""#2 利用abc模块定义抽象类 ABC叫抽象基类
from abc import ABC, abstractmethodclass Shape(ABC):# 利用装饰器把函数要长什么样子,抽象出来,这个就叫抽象方法@abstractmethoddef area(self,aaa):pass
# 这个Shape类,由于继承了抽象基类,所以就是一个抽象类
# 抽象类不能被实例化,只能被继承
# 被@abstractmethod修饰的方法就叫做抽象方法
# 所以这个Shape抽象类,就可以叫做接口
# 约束了子类必须要实现这个area方法,参数还要一样,不然就报错class Rectangle(Shape):def __init__(self, width, height):self.width = widthself.height = heightdef area(self,aaa):return self.width * self.heightclass Circle(Shape):def __init__(self, radius):self.radius = radiusdef area(self,aaa):return 3.14 * self.radius ** 2if __name__ == "__main__":# 抽象类不能直接实例化 Shape() 因为该抽象类里面有抽象方法# 报错:TypeError: Can't instantiate abstract class Shape with abstract methods area # 继承抽象类的子类可以正常实例化rectangle = Rectangle(3, 4)print(rectangle.area('haha')) # 12circle = Circle(5)print(circle.area('heihei')) #78.5
4 单例(Singleton)设计模式
# 单例(Singleton)设计模式
# 保证一个类只有一个实例,并提供一个访问它的全局访问点
# 优点:对唯一的实例受控访问# 什么情况下可能会用到单例模式:web框架的日志对象、数据库的连接器对象、操作系统的文件对象等# 简单又经典的实现单例的方法#--------------------方法1-------------------------#
#1 在需要单例的类里面
#2 定义一个类属性_instance = None
#3 写一个绑定给类的方法singleton
#4 以后想要用类生成对象,不要用类加括号生成对象
#5 用B.singleton()方法来得到对象,永远都是一个对象
#6 当你不想要单例模式的时候,就直接类加括号生成对象就行了from threading import Thread, Lock
import time
lock = Lock()class B:def __init__(self, name):self.name = nametime.sleep(1) # 模拟并发情况下,对象实例化时有网络延迟_instance = None@classmethoddef singleton(cls):if cls._instance is None:lock.acquire()if cls._instance is None:# 既然是单例模式,说明生成的对象的属性不会改变,# 所以在实例化的时候,传的参完全可以固定死cls._instance = cls('teng')lock.release()return cls._instancedef task():obj = B.singleton() # 只要用该方法生成对象,一直用的就是一个对象print(id(obj))for i in range(50):t = Thread(target=task, )t.start()#
# 高并发的情况下 前几个子线程,可能都判断出_instance为None
# 让先判断出_instance为None的子线程,抢锁,第一个抢到锁的,让类生成对象,并赋值给_instance属性,放锁,返回对象
# 这样第二个子线程在抢到锁后,再判断时,_instance已经有值了,直接放锁了
# 其他执行慢的子线程,在走到第一个判断时,发现有值,直接连抢锁的操作都不走了!!
# 后面的子线程就都没有抢锁放锁的操作了,直接返回对象了#----------------------方法1-----------------------##---------------------方法2------------------------#
# 在你需要单例的类里面,把这个__new__方法复制进去就行了
# 当然如果在并发的情况下,也可以像上面一样加锁来控制class Player:def __init__(self,id):self.id = id# 定义一个__new__方法,用于创建实例def __new__(cls,*args,**kwargs):# 判断实例是否存在,若不存在则创建实例if not hasattr(cls,'_instance'):father_cls = super() # 获取父类# 调用父类的__new__方法,也就是object类的__new__方法,传当前类的类名,就会产生类自己的空对象(骨架)cls._instance = father_cls.__new__(cls)# 返回实例return cls._instance# 类加括号会先执行元类的双下call方法,双下call方法里面,干3件事
#1 运行元类产生的类里面的__new__方法
#2 运行元类产生的类,也就是A类自己的__init__方法
#3 返回实例化的对象obj1 = Player(10)
obj2 = Player(20)
print(id(obj1),id(obj2)) # 内层地址一样
print(obj1.id) # 20
print(obj2.id) # 20#--------------------方法2-------------------------#
5 原型(Prototype)模式
"""通过复制现有的实例来创建新的实例。
原型模式(Prototype)是一种创建型设计模式,
它允许通过复制现有对象来创建新对象,而不是通过实例化类来创建对象。
原型模式允许我们创建一个原型对象,然后通过克隆这个原型对象来创建新的对象,从而避免了重复的初始化操作。在 Python 中,可以使用 copy 模块中的 copy() 和 deepcopy() 函数来实现原型模式。
copy() 函数执行的是浅复制,它复制对象本身,但不复制对象引用的内存空间,
因此如果原型对象中包含可变对象(如列表、字典等),那么新对象和原型对象将共享这些可变对象。deepcopy() 函数则执行深复制,它会递归地复制对象及其引用的所有对象,
因此新对象和原型对象不会共享任何对象。"""# 下面是一个简单的 Python 实现,演示了如何使用原型模式创建和克隆一个包含可变和不可变成员的对象import copyclass Prototype:def __init__(self, x, y, items):self.x = xself.y = yself.items = itemsdef clone(self):return copy.deepcopy(self)items = ['item1', 'item2', 'item3']
p_obj = Prototype(1, 2, items)
p_obj_clone = p_obj.clone()
print(f'Original: x={p_obj.x}, y={p_obj.y}, items={p_obj.items}')
# Original: x=1, y=2, items=['item1', 'item2', 'item3']print(f'Clone: x={p_obj_clone.x}, y={p_obj_clone.y}, items={p_obj_clone.items}')
# Clone: x=1, y=2, items=['item1', 'item2', 'item3']items.append('item4')
p_obj.x = 5
p_obj.y = 10
print(f'Original (updated): x={p_obj.x}, y={p_obj.y}, items={p_obj.items}')
# Original (updated): x=5, y=10, items=['item1', 'item2', 'item3', 'item4']print(f'Clone (not updated): x={p_obj_clone.x}, y={p_obj_clone.y}, items={p_obj_clone.items}')
# Clone (not updated): x=1, y=2, items=['item1', 'item2', 'item3']#
# 这个模式感觉作用不大
#
1 适配器(Adapter Pattern)模式
"""【将一个类的方法接口转换成客户希望的另一个接口。】适配器模式使原本由于接口不兼容而不能在一起工作的类可以一起工作
实现方式:类适配器:使用多继承对象适配器:使用组合适用场景:
想适用一个已经存在的类,而它的接口不符合你的要求,这个时候就需要一个适配器类,
来将不符合你的要求的,别人的接口,的使用代码,封装到函数里,你只要保证这个函数的名称与参数符合要求就行
这样外部就不用直接调别人的接口了,直接调你封装好的函数即可"""from abc import ABC, abstractmethodclass Payment(ABC):@abstractmethoddef pay(self, money):passclass Alipay(Payment):def pay(self, money):print(f'支付宝支付{money}元')class Wechatpay(Payment):def pay(self, money):print(f'微信支付{money}元')# 假设银联pay 这个类是另一个系统里面的
class Bankpay:def cost(self, money):print(f'银行卡支付{money}元')# 假设苹果pay 这个类是另一个系统里面的
class Applepay:def icost(self, money):print(f'苹果支付{money}元')# 把本来不兼容的接口cost,转成兼容的接口pay# 适配器类1 类适配器 通过继承实现代码复用
class BankpayAdapter(Payment,Bankpay):def pay(self, money):self.cost(money)BankpayAdapter().pay(100)
# 如果想要苹果的支付也兼容,就必须要再写一个适配器类,继承苹果支付才行#
# 这个通过性更高点
# 适配器类2 对象适配器 通过组合实现代码复用
class PaymentAdapter(Payment):def __init__(self,payment_obj):self.payment = payment_objdef pay(self, money):self.payment.icost(money)PaymentAdapter(Applepay()).pay(1000)
2 桥接(Bridge)模式
"""【将事物的两个维度分离,使它们都可以独立的变化。】
一个对象中,通过一个属性名与另一个对象关联,不用将两个维度的代码写在一起应用场景:当事物有两个维度上的表现,并且两个维度都可能扩展时
优点:优秀的扩展能力"""class Shape:passclass Line(Shape):passclass Rectangle(Shape):passclass Circle(Shape):pass#-------------------------#
class RedLine(Line):passclass RedCircle(Line):passclass BlueLine(Line):passclass BlueCircle(Line):pass#-------------------------# # 这种设计类的方式 形状和颜色强耦合到了一起了
# 扩展起来不方便,当形状很多时,增加一种颜色,就需要写很多该颜色形状的类,代码复用性不高#-------------------------------------------------#from abc import ABC,abstractmethodclass Color(ABC):@abstractmethoddef paint(self,shape_obj):passclass Shape(ABC):def __init__(self,color_obj:Color):self.color = color_obj # 通过属性与另一个对象关联@abstractmethoddef draw(self):pass# 矩形
class Rectangle(Shape):name = '矩形'def draw(self):# xxxx逻辑代码self.color.paint(self)# 圆形
class Circle(Shape):name = '圆形'def draw(self):# yyyy逻辑代码self.color.paint(self) # 红色
class Red(Color):def paint(self,shape_obj:Shape):print(f'{shape_obj.name}被涂成红色')# 绿色
class Green(Color):def paint(self,shape_obj:Shape):print(f'{shape_obj.name}被涂成绿色')# 实例化生成矩形对象的时候,传红色对象
rectangle_obj = Rectangle(Red())
rectangle_obj.draw() # 这样矩形对象画出来的就是红色的矩形了circle_obj = Circle(Green())
circle_obj.draw() # 画出来的就是绿色的圆形了
3 组合(Composite)模式
"""将对象组合成树形结构以表示“部分-整体”的层次结构。
组合模式使得用户对单个对象和组合对象的使用具有一致性适合场景:表示对象的“部分-整体”层次结构(特别是结构是递归的)希望用户忽略组合对象与单个对象的不同,用户统一地使用组合结构中的所有对象优点:定义了包含基本对象和组合对象的类层次结构简化了客户端代码,使客户端可以一致的使用组合对象和单个对象更容易增加新类型的组件"""from abc import ABC,abstractmethod# 抽象组件 保证叶子组件与复合组件都要有对应的方法
class Graphic(ABC):@abstractmethoddef draw(self):pass# 叶子组件 点
class Point(Graphic):def __init__(self,x,y):self.x = xself.y = ydef __str__(self) -> str:return f'点( {self.x}, {self.y} )'def draw(self):print(self)# 叶子组件 线
class Line(Graphic):def __init__(self,p1,p2):self.p1 = p1self.p2 = p2def __str__(self) -> str:return f'线段[ {self.p1} -> {self.p2} ]'def draw(self):print(self)line_obj = Line(Point(0,0),Point(1,1))
# print(line_obj) # 线段[ 点( 0, 0 ) -> 点( 1, 1 ) ]
# line_obj.draw() # 线段[ 点( 0, 0 ) -> 点( 1, 1 ) ]# 还可以写一些叶子组件 比如 圆 等# 复合组件 面
class Picture(Graphic):def __init__(self, iterable = None):self.children = [] # 记录父节点有哪些孩子节点对象if iterable:for g in iterable:self.add(g)def add(self,g):self.children.append(g)def draw(self):print('开始画图:')for g_obj in self.children:g_obj.draw()print('结束画图:')# 客户端
p1_obj = Point(2,3)
l1_obj = Line(Point(3, 4), Point(6, 7))
l2_obj = Line(Point(1, 5), Point(2, 8))
pic1_obj = Picture([p1_obj, l1_obj, l2_obj])p2_obj = Point(4,4)
l3_obj = Line(Point(1, 1), Point(9, 9))
pic2_obj = Picture([p2_obj, l3_obj])pic3_obj = Picture([pic1_obj, pic2_obj])
pic3_obj.draw()
4 外观(Facade)模式
"""对外提供一个统一的接口,来访问子系统中的一组接口,使得子系统更加容易使用
优点:减少系统相互依赖提高灵活性(方便客户端调用)提高安全性"""# 子系统1
class Cpu:def run(self):print('cpu is running')def stop(self):print('cpu is stop')# 子系统
class Memory:def run(self):print('memory is running')def stop(self):print('memory is stop')# 子系统3
class Disk:def run(self):print('disk is running')def stop(self):print('disk is stop')# 外观类
class Computer:def __init__(self):self.cpu = Cpu()self.memory = Memory()self.disk = Disk()def run(self):self.cpu.run()self.memory.run()self.disk.run()def stop(self):self.cpu.stop()self.memory.stop()self.disk.stop()c_obj = Computer()
c_obj.run()
c_obj.stop()# 通过一个对象的多个属性,去关联多个对象,并定义一些方法,这些方法里面,去使用关联的对象的某些方法
# 还是一个封装的思想,方便用户的使用
5 代理(Proxy Pattern)模式
"""为其它对象提供一个代理以便控制这个对象的访问应用场景:远程代理: 为远程的对象提供代理(比如ORM: 封装了数据库对象,提供了一个代理类,我们后端在向数据库查值或写值的时候,不需要去管数据库在不在远程,只需要操作ORM对象,让它去查值或写值即可)(对象数据不在本地服务器存在,就需要写一个远程代理的类,来为远程的对象提供代理)虚代理: 根据需要创建很大的对象保护代理:控制对原始对象的访问,用与对象有不同访问权限时优点:远程代理: 可以隐藏对象位于远程地址空间的事实虚代理: 可以进行优化保护代理: 允许在访问一个对象时有一些附加的业务处理"""from abc import ABC, abstractmethod# 抽象实体
class Subject(ABC):@abstractmethoddef get_content(self):pass@abstractmethoddef set_content(self, observer):pass# 实体
class RealSubject(Subject):def __init__(self, filename):self.filename = filenamef = open(filename, "r", encoding = "utf-8")print("读取文件内容")self.content = f.read()f.close()def get_content(self):return self.contentdef set_content(self, new_content):f = open(self.filename, "w", encoding = "utf-8")f.write(new_content)f.close()# 虚代理类
class VirtualProxy(Subject):def __init__(self, filename):self.filename = filenameself.subj = Nonedef get_content(self):if not self.subj:self.subj = RealSubject(self.filename)return self.subj.get_content()def set_content(self, new_content):if not self.subj:self.subj = RealSubject(self.filename)return self.subj.set_content(new_content)# 这里只是演示效果,不要关注代码的合理性
subj_obj = RealSubject("./test.txt")
# subj_obj.get_content()
# 不使用虚代理的时候,不使用get_content()方法,只要生成对象,就会去读取文件内容加载到内存中vir_subj_obj = VirtualProxy("./test.txt")
# 用了虚代理类后,生成的对象,不会去读取文件,只有在使用get_content()方法,才会去读取文件# 保护代理类 只允许用户读,不允许写
class ProtectedProxy(Subject):def __init__(self, filename):self.subj = RealSubject(filename)def get_content(self):return self.subj.get_content()def set_content(self, new_content):raise PermissionError("You don't have permission to modify the file")
6 装饰器(Decorator)模式
"""动态的给对象添加新的功能。装饰模式(Decorator)是一种结构型设计模式,它允许你在运行时为对象动态添加功能。
装饰模式是一种替代继承的方式,它通过将对象放入包装器对象中来实现这一点。
这种模式是开放封闭原则的一种具体实现方式。在装饰模式中,有一个抽象组件(Component)类,它定义了基本的操作方法。
有一个具体组件(ComponentA)类,它实现了抽象组件类中定义的操作方法。
还有一个装饰器(Decorator)类,它也实现了抽象组件类中定义的操作方法,
并且它包含一个指向抽象组件类的引用。此外,还有一个具体装饰器(DecoratorA)类,它扩展了装饰器类,以实现额外的功能。
装饰模式的核心思想是,【在不改变原有类的情况下,通过包装原有类来扩展其功能。】
这使得我们可以在运行时,动态地添加功能,而不需要在编译时修改代码。Component: 抽象组件类,定义了基本的操作方法。
Decorator: 装饰器类,实现了抽象组件类中定义的操作方法,并且它包含一个指向抽象组件类的引用。
ComponentA: 具体组件类,实现了抽象组件类中定义的操作方法。
DecoratorA: 具体装饰器类,扩展了装饰器类,以实现额外的功能。在装饰模式中,【可以通过组合的方式来添加多个装饰器,从而实现对对象的多次装饰。】
同时,装饰器对象可以嵌套在其他装饰器对象内部,从而形成一个装饰器对象的树形结构,这种结构称为装饰器链。
在执行操作时,装饰器对象会按照一定的顺序递归地调用装饰器链中的操作方法。下面是一个装饰模式的 Python 实现示例:
用类实现装饰器"""from abc import ABC, abstractmethod# 定义抽象组件
class Component(ABC):@abstractmethoddef operation(self):pass# 定义具体组件(就是正常使用的类)
class ComponentA(Component):def operation(self):print("执行正常函数的操作------")return "具体组件ComponentA"# 定义抽象装饰器
class Decorator:def __init__(self, component_obj):self._component = component_obj@abstractmethoddef operation(self):pass# 定义具体装饰器A
class DecoratorA(Decorator):def operation(self):print('装饰器A开始装饰---------')str1 = f"DecoratorA:( {self._component.operation()} )"print('装饰器A装饰完成---------')return str1# 定义具体装饰器B
class DecoratorB(Decorator):def operation(self):print('装饰器B开始装饰---------')str2 = f"DecoratorB:[ {self._component.operation()} ]"print('装饰器B装饰完成---------')return str2if __name__ == "__main__":component_a_obj = ComponentA() # 生成具体组件对象decoratorA_obj = DecoratorA(component_a_obj) # 生成装饰器对象A,并传具体组件对象decoratorB_obj = DecoratorB(decoratorA_obj) # 生成装饰器对象B,并传装饰器对象Aprint(decoratorB_obj.operation())"""装饰器B开始装饰---------装饰器A开始装饰---------执行正常函数的操作------装饰器A装饰完成---------装饰器B装饰完成---------DecoratorB:[ DecoratorA:( 具体组件ComponentA ) ]"""# 也就是说: 先将正常使用的类实例化,然后在实例化装饰器的类,并将使用的类的实例传入# 这样在运行装饰器类的实例的方法时,# 实现在运行被装饰的对象的方法前与后,可以运行装饰器对象的一些代码#-------------------------------------------#
6.2 函数装饰器(有点意思)
# 加在类上面的装饰器函数,实现给对象添加一些属性与方法def wrapper(cls):def inner(*args, **kwargs):_cls = clsobj = cls(*args, **kwargs)obj.name = 'lqz'def speak():print('装饰器说话了')# 怎么再去运行原对象里面的speak方法了?# 这里实际上是用类调用speak方法,并主动将person对象传进去# 所以原person对象能实现的功能,这里都能实现_cls.speak(obj) obj.speak = speakreturn objreturn inner@wrapper # 语法糖会把Person当参数传入到装饰器中 Person=wrapper(Person)
class Person:def __init__(self, hobby):self.hobby = hobbydef speak(self):print(f'人说话了1111111111')p = Person('篮球') # 实际上运行的是inner('篮球')print(p.name) # lqz
print(p.hobby) # 篮球p.speak()
# 装饰器说话了
# 人说话了1111111111#
# 这个函数装饰器,如果想要实现在运行被装饰的对象的speak方法前,添加一些额外操作
# 就可以再inner函数里面,定义一个和被装饰的类里面一样的speak方法
# 然后obj.speak = speak 把被装饰的对象的speak方法换掉
# 这样用户使用Person('篮球') 实际上返回的inner函数的返回值,也是person对象
# 然后用户用该对象去调speak方法,实际上运行的是inner里面的speak方法
# 但是inner里面的speak方法里面,最后我们又运行了类里面的speak方法,并把对象给传进去了
#
7 享元(Flyweight)模式
"""
通过共享技术来有效的支持大量细粒度的对象。享元模式(Flyweight)是一种结构型设计模式,
它通过共享对象来尽可能减少内存使用和对象数量。
在享元模式中,存在两种对象:内部状态(Intrinsic State)和外部状态(Extrinsic State)。
内部状态指对象的共享部分,不随环境改变而改变;
外部状态指对象的非共享部分,会随环境改变而改变。实现思路:享元模式的核心思想是尽量重用已经存在的对象,减少对象的创建和销毁,从而提高性能和节省内存。
它通常适用于需要大量创建对象的场景,但又不能因为对象过多而导致内存不足或性能降低的情况。下面是一个简单的享元模式的示例,假设我们有一个字符工厂,它可以创建不同的字符对象。
在实现字符对象时,我们发现有一些字符会被频繁使用,而且它们的状态是不变的,
例如空格、逗号、句号等标点符号。因此,我们可以将这些字符设计为享元对象,通过共享来节省内存。"""class CharacterFactory:def __init__(self):self.characters = {}def get_character(self, character):if character in self.characters:return self.characters[character]else:new_character = Character(character)self.characters[character] = new_characterreturn new_characterclass Character:def __init__(self, character):self.character = characterdef render(self, font):print(f"character 【{self.character}】 in font {font}")# 创建字符工厂
factory = CharacterFactory()# 获取不同的字符
char1 = factory.get_character("A")
char2 = factory.get_character("B")
char3 = factory.get_character("C")
char4 = factory.get_character(",")
char5 = factory.get_character(" ")
char6 = factory.get_character(".")# 渲染不同的字符
char1.render("Arial") # character 【A】 in font Arial
char2.render("Times New Roman") # character 【B】 in font Times New Roman
char3.render("Arial") # character 【C】 in font Arial
char4.render("Times New Roman") # character 【,】 in font Times New Roman
char5.render("Arial") # character 【 】 in font Arial
char6.render("Times New Roman") # character 【.】 in font Times New Roman"""
代码讲解:在上述示例中,我们创建了一个CharacterFactory类来管理字符对象。
当客户端需要获取一个字符时,可以调用get_character方法。
如果该字符已经被创建过了,就直接返回共享的对象;否则,创建一个新的对象并将其保存到工厂中,以备下次使用。
字符对象Character有一个render方法,用于渲染该字符。
在实际使用中,我们可能需要给不同的字符设置不同的字体,这里只是为了演示方便,用字符串代替了字体对象。通过享元模式,我们可以共享多个相同的字符对象,从而减少内存使用和对象数量。
在这个例子中,如果没有使用享元模式,我们可能需要创建多个空格、逗号和句号对象,而这些对象的状态都是不变的,这样就会导致内存浪费。
通过使用享元模式,我们可以将这些相同的对象共享起来,避免重复创建对象,从而提高性能和节省内存。需要注意的是,享元模式并不是万能的,它适用于需要大量创建相同对象的场景。
如果对象的数量不大,或者对象状态变化频繁,那么使用享元模式可能会增加代码复杂度,而且也不一定能够带来性能提升。
因此,在使用享元模式时需要仔细考虑是否适合当前场景。"""
1 责任链(Chain of Responsibility)模式
"""将请求的发送者和接收者解耦,使的多个对象都有处理这个请求的机会。
将这些对象连成一条链,并沿着这条链传递该请求,知道有一个对象处理它为止比如OA系统中的请假,根据请假的天数的不同,就会有不同的领导去审批你的请假条适用场景:有多个对象可以处理一个请求,哪个对象处理由运行时决定在不明确接收者的情况下,向多个对象中的一个对象,提交一个请求优点:降低耦合度: 一个对象无需知道,到底是哪个对象处理了他的请求"""from abc import ABC, abstractmethodclass Handler(ABC):@abstractmethoddef handle_leave(self):pass# 具体处理者1
class GeneralHandler(Handler):def handle_leave(self,day):if day < 10:print(f"总经理准假:{day}天")else:print("天数太多,辞职吧")# 具体处理者2
class DepartmentManager(Handler):def __init__(self):self.next = GeneralHandler()def handle_leave(self,day):if day <= 5:print(f"部门经理准假:{day}天")else:print("天数太多,部门经理职权不足")self.next.handle_leave(day)# 具体处理者3
class ProjectDirector(Handler):def __init__(self):self.next = DepartmentManager()def handle_leave(self,day):if day <= 2:print(f"项目主管准假:{day}天")else:print("天数太多,项目主管职权不足")self.next.handle_leave(day)# 不管请多少天,都是直接让责任链的最下层的处理者先去处理,天数长就会自动往上传给高层的对象处理
day = 4
p_obj = ProjectDirector()
p_obj.handle_leave(day)
2 观察者(Observer)模式 也叫发布订阅模式
"""定义对象间的一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。适用场景:当对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变的时候当一个对象必须通知其他对象,而它又不能假定其他对象是谁,换言之你不希望这些对象是紧耦合的优点:目标和观察者之间的抽象耦合最小支持广播通信"""from typing import List, Optional, Tuple, Union
from abc import ABC, abstractmethod# 抽象订阅者
class Observer(ABC):@abstractmethoddef update(self, notice_obj):pass# 抽象发布者
class Publisher:def __init__(self):self._observers:Optional[list[Observer]] = []# 订阅 添加观察着def attach(self, observer):self._observers.append(observer)# 取消订阅 删掉观察者def detach(self, observer):self._observers.remove(observer)# 推送消息def notify(self):for observer in self._observers:observer.update(self)# 具体发布者 比如老板
class PublisherBoss(Publisher):def __init__(self, init_company_info = None):super().__init__()self.__company_info = init_company_info# 方法伪装成属性,当用户使用该属性名的时候,触发下面函数的运行@propertydef company_info(self):return self.__company_info# 当用户使用伪装的属性进行赋值操作的时候,触发下面函数的运行@company_info.setterdef company_info(self, info):self.__company_info = info# 关键代码self.notify() # 具体的发布者对象,调用父类的notify方法,给每个订阅者发通知消息# obj = StaffNotice('abcdefg')
# print(obj.company_info) # abcdefg
# obj.company_info = 'hijklmn'
# print(obj.company_info) # hijklmn# 具体订阅者 比如员工
class ObserverStaff(Observer):def __init__(self):self.company_info = Nonedef update(self, publisher_boss_obj:PublisherBoss):# 将具体的发布者对象的数据,赋值给具体的订阅者对象self.company_info = publisher_boss_obj.company_info# 创建具体的发布者
Publisher_boss_obj = PublisherBoss('初始信息:hahaha')
s1 = ObserverStaff()
s2 = ObserverStaff()
Publisher_boss_obj.attach(s1) # 具体发布者将观察者对象,添加到观察者列表里
Publisher_boss_obj.attach(s2)
print(s1.company_info) # NonePublisher_boss_obj.company_info = '公司准备上市'
print(s1.company_info) # 公司准备上市
print(s2.company_info) # 公司准备上市Publisher_boss_obj.detach(s2) # 移除s2
Publisher_boss_obj.company_info = '公司上市成功'
print(s1.company_info) # 公司上市成功
print(s2.company_info) # 公司准备上市# 归根结底就一句话,发布者对象要有一个属性,该属性对应一个列表,该列表包含所有已订阅的订阅者对象
# 然后发布者对象还要有一个推送消息的方法,一旦发布者需要将信息发布,就调用该方法
# 该方法就是遍历列表里的每一个订阅对象,将消息推送给每一个订阅对象
3 策略(Strategy)模式
"""定义一系列算法,把它们一个个封装起来,并且使它们可以相互替换。
优点:定义了一系列可重用的算法和行为消除了一些条件语句可以提供相同行为的不同实现
缺点:客户必须要了解不同的策略,知道怎么去切换不同的策略比如打车软件的根据用户发布的打车信息,匹配最佳的司机 的算法,
可能有多种算法,比如第一种算法可以算出第一最佳的司机,但是耗时较长
第二种算法为近似算法,算出的可能为第一到第五的最佳司机中的任意一个,优点用时较短
所以可能打车高峰期,会采取第二种算法,加快处理速度
打车低峰期采取第一种算法,给用户更好的体验 这种就是策略切换"""from abc import ABC, abstractmethodclass Strategy(ABC):@abstractmethoddef execute(self,data):passclass StrategyA(Strategy):def execute(self, data):print(f"使用慢策略A: {data}")class StrategyB(Strategy):def execute(self, data):print(f"使用快策略B: {data}")# 上下文类 把切换与执行策略的逻辑写在里面
class Context:def __init__(self, data, strategy):self.data = dataself._strategy = strategy# 切换策略def set_strategy(self, strategy):self._strategy = strategy# 执行策略def do_strategy(self):self._strategy.execute(self.data)#
data = "hhhhhhhh"strategy_A_obj = StrategyA()
strategy_B_obj = StrategyB()# 生成上下文类,并绑定策略对象
context_obj = Context(data, strategy_A_obj)
context_obj.do_strategy() # 使用慢策略A: hhhhhhhh# 切换策略
context_obj.set_strategy(strategy_B_obj)
context_obj.do_strategy() # 使用快策略B: hhhhhhhh
4 模板方法(Template Method)模式
"""定义一个操作中的算法骨架,而将一些步骤延迟到子类中。
模版方法使得子类可以不改变一个算法的结构,即可重定义该算法的的某些特定步骤适用场景:一次性实现一个算法的不变的部分各个子类中的公共行为应该被提取出来(模板方法),并集中到一个公共父类中以避免代码重复控制子类扩展"""from abc import ABC, abstractmethod
import time# 抽象类
class Window(ABC):# 类里 定义的抽象方法就是原子操作或者叫钩子操作@abstractmethoddef start(self):pass@abstractmethoddef repaint(self):pass @abstractmethoddef stop(self):pass# 模板方法作为算法的骨架def run(self):self.start()while True:try:self.repaint()time.sleep(1)except KeyboardInterrupt:breakself.stop()# 具体类实现原子操作
class MyWindow(Window):def __init__(self,msg):self.msg = msgdef start(self):print("窗口开始运行")def stop(self):print("窗口停止运行")def repaint(self):print(self.msg)#
MyWindow("hello...").run()
"""
窗口开始运行
hello...
hello...
hello...
hello...
窗口停止运行
"""
4 中介者(Mediator)模式
用一个中介对象来封装一系列的对象交互。
5 命令(Command)模式
"""
将命令请求封装为一个对象,使得可以用不同的请求来进行参数化。
【主要用来做桌面程序的】命令模式(Command)是一种行为型设计模式,它将请求封装成一个对象,
从而使您可以将不同的请求与其请求的接收者分开。
这种模式的目的是通过将请求发送者和请求接收者解耦来实现请求的发送、执行和撤销等操作。实现思路:
命令模式涉及以下角色:#1 Command接口:定义了一个执行命令的方法 execute。
#2 具体命令类:实现了Command接口,实现 execute 方法,就是让执行命令对象运行相关的方法。
#3 管理命令类:负责管理一个个具体的命令对象,可以在需要时,让管理的多个具体命令对象运行execute方法。
#4 执行命令类:包含一些特定于应用程序的业务逻辑,实际执行命令。"""# 下面是一个简单的 Python 实现示例:from abc import ABC, abstractmethod
from typing import List, Optional, Tuple, Union# 1、定义 Command 接口:
class Command(ABC):@abstractmethoddef execute(self):pass# 2.1、实现具体命令类:
class LightOnCommand(Command):def __init__(self, do_command_obj):self.do_command = do_command_obj # 用于关联执行命令对象def execute(self):self.do_command.turn_on()# 2.2、实现具体命令类:
class LightOffCommand(Command):def __init__(self, do_command_obj):self.do_command = do_command_obj # 用于关联执行命令对象def execute(self):self.do_command.turn_off()# 3、定义 管理命令类:
class HandleCommand:def __init__(self):self.commands:list[Command] = [] # 用于存放命令对象# 用于将命令对象添加到列表中def add_command(self, command):self.commands.append(command)# 用于将列表中的命令对象拿出来,并执行命令对象的excute方法 def execute_commands(self):for command in self.commands:command.execute()# 4、定义 执行命令类
class DoCommand:def turn_on(self):print("The light is on")def turn_off(self):print("The light is off")do_command_obj = DoCommand() # 创建执行命令对象
handle_command_obj = HandleCommand() # 创建管理命令对象# 先生成具体命令对象,并与执行命令对象关联
# 然后在用,管理命令对象,运行add_command方法,并传具体命令对象
# 这样也就是,管理命令对象的commands列表里,就有了一个个待执行的具体命令对象了
handle_command_obj.add_command(LightOnCommand(do_command_obj))
handle_command_obj.add_command(LightOffCommand(do_command_obj))# 管理命令对象运行execute_commands方法,
# 就行去让每一个待执行的具体命令对象,运行execute方法
# 最终就是运行执行命令对象里的方法
send_command_obj.execute_commands()"""
当我们执行这个程序时,它将输出以下内容:
The light is on
The light is off代码解释:我们先创建了一个 do_command_obj 对象,
然后使用 LightOnCommand 和 LightOffCommand 对象分别绑定该对象的某个方法【通过将具体命令对象 和 命令的执行对象分开】
我们可以轻松地添加、删除和替换命令,
同时也使得程序更加灵活和可扩展。总的来说,命令模式提供了一种通过将请求封装成对象来实现请求的发送、执行和撤销的方法,
从而使得 命令对象 和 命令执行对象解耦,提高程序的灵活性和可扩展性。"""
6 解释器(Interpreter)模式
"""给定一个语言,定义它的文法的一种表示,并定义一个解释器。【主要用来做编译器的】解释器模式(Interpreter Pattern)是一种行为型设计模式,
它定义了一种语言文法,以及一个解释器,用于解释该语言中的句子。
解释器模式通常用于解决特定类型的问题,例如解释计算器表达式,SQL 查询语句等。解释器模式包括三个核心角色:Context(上下文):它是解释器的运行环境。它存储解释器所需的一些全局信息。
Abstract Expression(抽象表达式):它是定义所有表达式的接口,通常包含解释方法 interpret()。
Concrete Expression(具体表达式):它实现抽象表达式接口,用于解释特定类型的表达式。下面是解释器模式的 Python 实现示例:
"""from abc import ABC, abstractmethod
from typing import List, Optional, Tuple, Union# Context类来表示解释器的运行环境
class Context:def __init__(self):self._variables = {} # 存变量的值的字典# 给变量赋值def set_variable(self, name, value):self._variables[name] = value# 获取变量的值def get_variable(self, name):return self._variables.get(name)# 抽象表达式类
class Expression(ABC):@abstractmethoddef interpret(self, context_obj):pass# 具体表达式类,用于解释变量。
class VariableExpression(Expression):def __init__(self, name):self._name = name# 通过上下文对象拿到变量所赋的值def interpret(self, context_obj:Context):return context_obj.get_variable(self._name)# 具体表达式类,用于解释常量。
class ConstantExpression(Expression):def __init__(self, value):self._value = valuedef interpret(self, context_obj:Context):# 这里虽然用不到上下文对象,但是由于继承了Expression抽象类,必须要这么写return self._value# 具体表达式类,用于解释加法表达式。
class AddExpression(Expression):def __init__(self,left:Union[VariableExpression,ConstantExpression,'AddExpression'], right:Union[VariableExpression,ConstantExpression,'AddExpression']):self._left = leftself._right = rightdef interpret(self, context):# 这里不管_left与_right是变量还是常量,都是统一调用自己的interpret方法,拿到自己对应的值,然后再相加return self._left.interpret(context) + self._right.interpret(context)# 具体表达式类,用于解释减法表达式。
class SubtractExpression(Expression):def __init__(self, left, right):self._left = leftself._right = rightdef interpret(self, context):return self._left.interpret(context) - self._right.interpret(context)# 客户端测试代码
if __name__ =="__main__":context = Context() # 先生成上下文对象,表示解释器的运行环境const_a_obj = ConstantExpression(1)const_b_obj = ConstantExpression(2)const_c_obj = ConstantExpression(3) # 先生成3个常量对象x = VariableExpression('x')y = VariableExpression('y') # 再生成2个变量对象# 通过上下文类, 给变量赋值context.set_variable('x', 10) context.set_variable('y', 5) # 1 + 2 + 3 = 6add_expr_obj1 = AddExpression(const_a_obj, const_b_obj) # 先生成一个表达式对象1add_expr_obj2 = AddExpression(add_expr_obj1, const_c_obj) # 再生成一个表达式对象2result = add_expr_obj2.interpret(context)# 表达式对象2调用interpret方法, 运行self._left.interpret(context)时, 就会再去运行表达式对象1的interpret方法# 这样就实现了,先运行const_a_obj的interpret方法,再运行const_b_obj的interpret方法,然后结果再相加# 然后再运行const_c_obj的interpret方法,最后再将结果相加print(result)# 10 - 2 + 5 = 13expression = AddExpression(SubtractExpression(x, const_b_obj), y)result = expression.interpret(context)print(result)#
7 迭代器模式(Iterator)
"""迭代器模式(Iterator)是一种行为型设计模式,
它允许你在不暴露集合底层实现的情况下遍历集合中的所有元素。实现思路:在迭代器模式中,集合类(如列表、树等)将遍历操作委托给一个迭代器对象,而不是直接实现遍历操作。
迭代器对象负责实现遍历操作,以及保存当前遍历位置等状态。
这样,集合类就可以将遍历操作与集合底层实现解耦,从而使得集合类更加简单、灵活和易于维护。迭代器模式通常由以下几个角色组成:
迭代器(Iterator):定义了迭代器的接口,包含用于遍历集合元素的方法,如 next()、has_next() 等。
具体迭代器(ConcreteIterator):实现了迭代器接口,负责实现迭代器的具体遍历逻辑,以及保存当前遍历位置等状态。
集合(Aggregate):定义了集合的接口,包含用于获取迭代器对象的方法,如 create_iterator() 等。
具体集合(ConcreteAggregate):实现了集合接口,负责创建具体迭代器对象,以便遍历集合中的元素。迭代器模式的优缺点包括:
将遍历操作与集合底层实现解耦,使得集合类更加灵活和易于维护。
简化了集合类的接口,使得集合类更加简单明了。
提供了对不同类型的集合统一遍历的机制,使得算法的复用性更加高。迭代器模式的缺点是,由于迭代器对象需要保存遍历位置等状态,因此它可能会占用比较大的内存。
此外,由于迭代器对象需要负责遍历逻辑,因此它可能会变得比较复杂。以下是迭代器模式的一个简单示例,实现了一个列表类和一个列表迭代器类:
"""from typing import List, Optional, Tuple, Union
from abc import ABC, abstractmethod# 抽象迭代器类
class Iterator(ABC):# 用于判断是否还有下一个元素。@abstractmethoddef has_next(self)->bool:pass# 用于返回下一个元素。@abstractmethoddef next(self):pass# 具体迭代器类
class ConcreteIterator(Iterator):def __init__(self, data: Union[list, dict, tuple]):self.data = dataself.index = 0def has_next(self):return self.index < len(self.data)def next(self):if self.has_next():value = self.data[self.index]self.index += 1return value# 抽象集合类
class Aggregate(ABC):@abstractmethoddef create_iterator(self)->Iterator:pass# 具体集合类
class ConcreteAggregate(Aggregate):def __init__(self, data: Union[list, dict, tuple]):self.data = datadef create_iterator(self):return ConcreteIterator(self.data)# 测试
if __name__ == "__main__":data = [1, 2, 3, 4, 5]conc_aggre_obj = ConcreteAggregate(data) # 生成具体集合对象# 具体集合对象再调用create_iterator函数生成具体迭代器对象,主要就是把data数据传给了迭代器对象conc_iterator_obj = conc_aggre_obj.create_iterator()# 具体迭代器对象开始迭代取值while conc_iterator_obj.has_next():print(conc_iterator_obj.next())#
#
8 中介者(Mediator)模式
"""
用一个中介对象来封装一系列的对象交互。
中介者模式(Mediator)是一种行为型设计模式,
它用于将多个对象之间的交互解耦,从而使得对象之间的通信更加简单和灵活。实现思路:
在中介者模式中,多个对象之间不直接相互通信,而是通过一个中介者对象进行通信。
这样,每个对象只需要和中介者对象通信,而不需要知道其他对象的存在。
中介者对象负责协调各个对象之间的交互,使得系统更加灵活和易于维护。中介者模式通常由以下几个角色组成:
抽象中介者(Mediator):定义了各个同事对象之间交互的接口,通常包含一个或多个抽象方法,用于定义各种交互操作。
具体中介者(ConcreteMediator): 负责协调各个同事对象之间的交互关系。抽象同事类(Colleague): 定义了各个同事对象的接口,包含一个指向中介者对象的引用,以便与中介者进行通信。
具体同事类(ConcreteColleague): 负责实现各自的行为,并且需要和中介者对象进行通信。中介者模式的优点:
解耦了各个对象之间的交互关系,使得系统更加灵活和易于维护。
降低了系统的复杂度,使得各个对象之间的交互变得简单明了。
可以集中管理各个对象之间的交互关系,从而提高系统的可维护性和可扩展性。中介者模式的缺点:
由于中介者对象需要负责协调各个同事对象之间的交互关系,因此它的职责可能会变得非常复杂。
另外,由于中介者对象需要了解各个同事对象之间的交互关系,因此它可能会变得比较庞大。下面是一个简单的中介者模式的 Python 实现,该实现使用一个聊天室作为中介者,多个用户作为同事类:
"""from typing import List# 同事类
class User:def __init__(self, name: str, mediator_obj:'ChatRoom'):self.name = nameself.mediator = mediator_objdef send_message(self, message: str):self.mediator.send_message(message, self)def receive_message(self, message: str):print(f"{self.name} received message: {message}")# 中介者类
class ChatRoom:def __init__(self):self.users: List[User] = []def add_user(self, user: User):self.users.append(user)def send_message(self, message: str, sender: User):for user in self.users: if user != sender:user.receive_message(f"{sender.name}: {message}")if __name__ == '__main__':# 生成中介者对象chat_room = ChatRoom()# 生成同事对象,并关联中介者对象alice = User("Alice", chat_room)bob = User("Bob", chat_room)charlie = User("Charlie", chat_room)# 将同事对象添加到中介者对象的users列表中chat_room.add_user(alice)chat_room.add_user(bob)chat_room.add_user(charlie)alice.send_message("Hi everyone!")# 实际上是通过中介者对象去发消息,而且把自己也传过去了,保证自己不会给自己发消息# Bob received message: Alice: Hi everyone!# Charlie received message: Alice: Hi everyone!bob.send_message("Hello Alice!")# Alice received message: Bob: Hello Alice!# Charlie received message: Bob: Hello Alice!charlie.send_message("Hey guys, what's up?")# Alice received message: Charlie: Hey guys, what's up?# Bob received message: Charlie: Hey guys, what's up?#
#
9 备忘录(Memento Pattern)模式
"""在不破坏封装的前提下,保持对象的内部状态。备忘录模式(Memento)是一种行为型设计模式,
它允许在不暴露对象实现细节的情况下保存和恢复对象的内部状态。
备忘录模式的核心是备忘录类,它用于存储对象的状态信息,同时提供给其他类访问状态信息的接口。备忘录模式包括三个核心角色:
Originator(发起人):它是需要保存状态的对象。它创建备忘录对象来存储内部状态,并可以使用备忘录对象来恢复其先前的状态。Memento(备忘录):它是存储发起人对象内部状态的对象。备忘录对象由发起人创建,并由发起人决定何时读取备忘录以恢复其先前的状态。Caretaker(管理者):它负责备忘录的安全保管。它只能将备忘录传递给其他对象,不能修改备忘录的内容。在 Python 中,备忘录模式通常使用Python内置copy模块 和dict属性来实现。下面是一个简单的备忘录模式的 Python 实现:
"""import copy# 发起人类
class Originator:def __init__(self):self._state = Nonedef set_state(self, state):print("设置状态为:", state)self._state = state# 创建备忘录对象,来保存当前对象的状态def create_memento(self):print("创建备忘录")return Memento(copy.deepcopy(self._state))# 用指定备忘录对象保存的数据,替换当前对象的状态def restore_memento(self, memento_obj:'Memento'):print("恢复备忘录")self._state = memento_obj.get_state() # 备忘录对象保存的数据,替换当前对象的状态def show_state(self):print("当前状态为:", self._state)# 备忘录类
class Memento:def __init__(self, state):self._state = statedef get_state(self):return self._state# 管理者类
class Caretaker:def __init__(self):self._mementos = []def add_memento(self, memento_obj:Memento):self._mementos.append(memento_obj)def get_memento(self, index:int):return self._mementos[index]# 测试
if __name__ == "__main__":originator_obj = Originator() # 创建发起人对象caretaker_obj = Caretaker() # 创建管理者对象originator_obj.set_state("状态1")memento_obj1 = originator_obj.create_memento() # 创建备忘录对象,将发起者的状态信息保存到备忘录对象中caretaker_obj.add_memento(memento_obj1) # 管理者对象将备忘录对象添加到_mementos列表中# 设置状态为: 状态1# 创建备忘录originator_obj.set_state("状态2")memento_obj2 = originator_obj.create_memento() # 创建备忘录对象,将发起者的状态信息保存到备忘录对象中caretaker_obj.add_memento(memento_obj2) # 管理者对象将备忘录对象添加到_mementos列表中# 设置状态为: 状态2# 创建备忘录originator_obj.set_state("状态3")originator_obj.show_state()# 设置状态为: 状态3# 当前状态为: 状态3originator_obj.restore_memento(caretaker_obj.get_memento(1)) # 用索引为1的备忘录对象,恢复发起者对象的状态originator_obj.show_state()# 恢复备忘录# 当前状态为: 状态2originator_obj.restore_memento(caretaker_obj.get_memento(0))originator_obj.show_state()# 恢复备忘录# 当前状态为: 状态1#
#
#
10 状态(State Pattern)模式
"""
允许一个对象在其对象内部状态改变时改变它的行为。状态模式(State)是一种行为型设计模式,它允许对象在不同的内部状态下改变其行为。
在状态模式中,一个对象可以有多个状态,每个状态都对应着一组不同的行为。
对象根据自身的状态,选择不同的行为。
这种模式将状态抽象成独立的类,使得状态之间可以相互切换,而不影响对象的整体行为。状态模式由三个核心组件构成:
上下文环境(Context):表示当前对象的状态,它维护一个对抽象状态类的引用,以便能够切换状态。
抽象状态(State):定义一个接口,用于封装与环境相关的行为。
具体状态(ConcreteState):实现抽象状态接口,实现与环境相关的行为。在状态模式中,当对象的状态发生变化时,它会将状态的处理委托给当前状态对象。
状态对象会负责处理相关的操作,并且在必要时会将环境的状态切换到新的状态。状态模式的优点:
将状态转换的逻辑封装在状态类中,使得状态之间的切换变得简单明了。
增加新的状态非常容易,只需要增加新的状态类即可。
可以消除大量的条件分支语句,使得代码更加清晰和易于维护。
状态模式的缺点:
由于需要创建多个状态类,因此会增加系统的复杂度。
另外,状态之间的转换也需要仔细设计,否则可能会导致系统的不稳定性。下面是一个简单的使用 Python 实现状态模式的例子。假设我们有一个电梯,它可以处于三种状态之一:开门状态、关门状态和运行状态。
在每种状态下,电梯的行为不同。
我们可以使用状态模式来管理电梯的不同状态,从而使电梯的行为更加清晰和易于维护。"""from typing import List,Optional,Union# 定义一个抽象状态类 State,
# 包含一个抽象方法 handle,用于处理电梯在不同状态下的行为:
class State:def handle(self):pass# 定义三个具体状态类
# 电梯的开门状态
class OpenState(State):def handle(self):print("Opening the door")# 电梯的关门状态
class CloseState(State):def handle(self):print("Closing the door")# 电梯的运行状态
class RunState(State):def handle(self):print("Running")# 定义一个上下文环境类ContextLift,它包含一个状态变量 state,表示当前电梯的状态。
class ContextLift:def __init__(self):self.state = Nonedef setState(self, state_obj:State):self.state = state_obj# 调用下面3个函数前,要先调用上面的setState函数# 电梯切换成开的状态def open(self):if self.state:self.state.handle()# 电梯切换成关的状态def close(self):if self.state:self.state.handle()def run(self):if self.state:self.state.handle()# 使用示例
if __name__ == "__main__":# 模拟电梯的运行过程,先生成上下文环境对象context_obj = ContextLift()# 首先将电梯的状态设置为开门状态,然后依次执行关门和运行操作:context_obj.setState(OpenState())context_obj.open()# Opening the doorcontext_obj.setState(CloseState())context_obj.close()# Closing the doorcontext_obj.setState(RunState())context_obj.run()# Running"""
这样,我们就成功地使用状态模式来管理电梯的不同状态。
在实际应用中,我们可以将更复杂的状态和行为加入到电梯系统中,从而使其更加灵活和易于扩展。
"""
11 访问者(Visitor Pattern)模式
"""
不改变数据结构的前提下,增加作用于一组对象元素的新功能。
访问者模式(Visitor)是一种行为型设计模式,它可以将算法与其所作用的对象分离开来。
这种模式允许你在不改变现有对象结构的情况下向对象结构中添加新的行为。实现思路:
访问者模式的核心思想是:将算法封装到访问者对象中,然后将访问者对象传递给对象结构中的元素,
以便这些元素可以调用访问者对象中的算法。
访问者对象可以通过访问元素中的数据和操作来实现算法,从而避免了对元素结构的直接访问。
访问者模式通常由以下几个角色组成:访问者(Visitor):定义了用于访问元素的方法,以便针对不同类型的元素采取不同的行为。具体访问者(ConcreteVisitor):实现了访问者接口,提供了算法的具体实现。元素(Element):定义了用于接受访问者的方法,这些方法通常以 accept() 的形式出现,以便元素可以将自己作为参数传递给访问者对象。具体元素(ConcreteElement):实现了元素接口,提供了具体的数据和操作,同时也提供了接受访问者的方法。结构类(Structure):定义了元素的集合,可以提供一些方法以便访问者能够遍历整个集合。访问者模式的优点:
可以将算法与其所作用的对象分离开来,避免了对元素结构的直接访问。
在访问者中可以实现对元素数据和操作的访问和处理,从而可以方便地扩展新的操作和处理逻辑。
可以方便地实现元素结构的复杂算法,而不需要修改元素结构本身。访问者模式的缺点:
它可能会导致访问者对象的复杂性增加。此外,它也可能会导致元素结构的扩展性变得比较差,
因为每当添加一个新的元素类型时,都需要修改所有的访问者对象。"""# 下面是一个简单的访问者模式的 Python 实现:from abc import ABC, abstractmethod# 抽象访问者类 定义了用于访问元素的方法
class Visitor(ABC):@abstractmethoddef visit_element_a(self, element_a):pass@abstractmethoddef visit_element_b(self, element_b):pass# 抽象元素类 定义了用于接受访问者的方法
class Element(ABC):@abstractmethoddef accept(self, visitor):pass# 具体元素类A
class ElementA(Element):def __init__(self, value):self.value = value# 也就是让对应的访问者对象,调用访问a元素对象的方法,并把a元素对象传进去# 也就是说a元素对象,如果想要接受访问者C对象的访问,访问者C对象必须也要有visit_element_a方法def accept(self, visitor:Visitor):visitor.visit_element_a(self)# 具体元素类B
class ElementB(Element):def __init__(self, value):self.value = value# 也就是让对应的访问者对象,调用访问b元素对象的方法,并把b元素对象传进去def accept(self, visitor:Visitor):visitor.visit_element_b(self)# 具体访问者类A
class VisitorA(Visitor):# 定义访问a元素对象的方法def visit_element_a(self, element_a:ElementA):print("VisitorA is visiting ElementA, value = ", element_a.value)# 定义访问B元素对象的方法def visit_element_b(self, element_b:ElementB):print("VisitorA is visiting ElementB, value = ", element_b.value)# 具体访问者类B
class VisitorB(Visitor):# 定义访问a元素对象的方法def visit_element_a(self, element_a:ElementA):print("VisitorB is visiting ElementA, value = ", element_a.value)# 定义访问B元素对象的方法def visit_element_b(self, element_b:ElementB):print("VisitorB is visiting ElementB, value = ", element_b.value)# 结构类
class Structure:def __init__(self):self.elements = []def add(self, element):self.elements.append(element)def delete(self, element):self.elements.remove(element)# 遍历列表里包含的每一个元素对象,# 然后让元素对象调用自己的accept方法,并传接受的具体访问者对象def accept(self, visitor:Visitor):for element in self.elements:element.accept(visitor)# 测试
if __name__ == "__main__":structure_obj = Structure() # 创建结构对象element_a = ElementA("A") # 创建具体元素A对象element_b = ElementB("B") # 创建具体元素B对象structure_obj.add(element_a) structure_obj.add(element_b) # 将具体元素对象添加到结构对象的elements列表中visitor_a = VisitorA() # 创建具体访问者A对象visitor_b = VisitorB() # 创建具体访问者B对象structure_obj.accept(visitor_a)# 遍历elements列表,让每一个具体元素对象调用accept方法,并传具体访问者对象A"""VisitorA is visiting ElementA, value = AVisitorA is visiting ElementB, value = B"""structure_obj.accept(visitor_b)# 遍历elements列表,让每一个具体元素对象调用accept方法,并传具体访问者对象B"""VisitorB is visiting ElementA, value = AVisitorB is visiting ElementB, value = B"""