设计模式之结构型模式

这些模式关注对象之间的组合和关联方式,以便形成更大的结构和功能。

  • 适配器模式(Adapter Pattern)
  • 桥接模式(Bridge)
  • 装饰器模式(Decorator)
  • 组合模式(Composite)
  • 外观模式(Facade)
  • 享元模式(Flyweight)
  • 代理模式(Proxy)

适配器模式(Adapter Pattern)

将一个类的接口转换成客户端所期望的另一个接口。
这种模式通常用于解决两个不兼容接口之间的兼容性问题。(充电线的转接头的功能)

一个生活中的实例是使用适配器模式连接不同类型的电源插头和电源插座。假设你有一个美国制造的电器,它使用美国标准的两脚插头(Type A),而你的墙上只有中国标准的三脚插座(Type I)。为了让电器能够在中国使用,你需要一个适配器来连接两者。
以下是一个简单的代码示例,展示了如何使用适配器模式来连接不同类型的插头和插座:

# 目标接口
class SocketAdapter:def __init__(self, plug):self.plug = plugdef connect(self):pass# 中国标准插座
class ChinaSocket:def connect_china_socket(self):print("连接中国标准插座")# 美国标准插头
class USPlug:def connect_us_plug(self):print("连接美国标准插头")# 适配器类
class ChinaToUSAdapter(SocketAdapter):def connect(self):self.plug.connect_china_socket()# 客户端代码
if __name__ == "__main__":china_socket = ChinaSocket()adapter = ChinaToUSAdapter(china_socket)adapter.connect()

在上述代码中,我们定义了一个目标接口 SocketAdapter,它声明了一个 connect 方法。然后,我们有一个中国标准插座类 ChinaSocket,它具有一个 connect_china_socket 方法,用于连接中国标准插座。

接下来,我们有一个美国标准插头类 USPlug,它具有一个 connect_us_plug 方法,用于连接美国标准插头。

然后,我们创建了一个适配器类 ChinaToUSAdapter,它继承了目标接口 SocketAdapter。适配器类内部持有一个中国标准插座对象 china_socket,并实现了目标接口的 connect 方法,该方法通过适配器调用中国标准插座的连接方法。

最后,我们在客户端代码中创建了中国标准插座对象 china_socket,并将其传递给适配器类的构造函数创建适配器对象 adapter。然后,我们调用适配器对象的 connect 方法,它会通过适配器调用中国标准插座的连接方法。

通过适配器模式,我们可以使用适配器对象连接不同类型的插头和插座,使得它们能够兼容并正常工作。这样,我们就实现了不同类型的插头和插座之间的兼容性。

桥接模式(Bridge)

把抽象化与实现化解耦

# 桥接模式的实现# 实现部分的接口
class DrawingAPI:def draw_circle(self, x, y, radius):pass# 具体实现部分A
class DrawingAPIA(DrawingAPI):def draw_circle(self, x, y, radius):print(f"在坐标({x}, {y})处以半径{radius}绘制圆形(使用实现A)")# 具体实现部分B
class DrawingAPIB(DrawingAPI):def draw_circle(self, x, y, radius):print(f"在坐标({x}, {y})处以半径{radius}绘制圆形(使用实现B)")# 抽象部分
class Shape:def __init__(self, drawing_api):self.drawing_api = drawing_apidef draw(self):pass# 具体抽象部分1
class CircleShape(Shape):def __init__(self, x, y, radius, drawing_api):super().__init__(drawing_api)self.x = xself.y = yself.radius = radiusdef draw(self):self.drawing_api.draw_circle(self.x, self.y, self.radius)# 具体抽象部分2
class SquareShape(Shape):def __init__(self, x, y, side_length, drawing_api):super().__init__(drawing_api)self.x = xself.y = yself.side_length = side_lengthdef draw(self):self.drawing_api.draw_square(self.x, self.y, self.side_length)# 客户端代码
if __name__ == "__main__":circle_a = CircleShape(1, 2, 3, DrawingAPIA())circle_a.draw()circle_b = CircleShape(4, 5, 6, DrawingAPIB())circle_b.draw()

在这个示例中,我们使用了桥接模式来将抽象部分(Shape)和实现部分(DrawingAPI)解耦。抽象部分定义了基本的形状(如圆形和正方形),而实现部分定义了具体的绘制方法。
通过将抽象部分和实现部分组合在一起,我们可以轻松地在运行时选择不同的实现,而无需更改抽象部分的代码。这样可以实现更灵活和可扩展的代码结构。
在上述示例中,我们创建了两个具体的实现部分:DrawingAPIADrawingAPIB,它们分别用于绘制圆形。然后,我们创建了两个具体的抽象部分:CircleShape,分别使用不同的实现部分进行绘制。

如果不使用桥接模式进行解耦,代码可能会变得更加紧密耦合,具体实现部分将直接嵌入到抽象部分中。以下是一个示例,展示了没有使用桥接模式的情况:

# 没有使用桥接模式的紧密耦合示例# 圆形类
class CircleShape:def __init__(self, x, y, radius):self.x = xself.y = yself.radius = radiusdef draw(self):# 在这里直接实现绘制圆形的逻辑print(f"在坐标({self.x}, {self.y})处以半径{self.radius}绘制圆形")# 正方形类
class SquareShape:def __init__(self, x, y, side_length):self.x = xself.y = yself.side_length = side_lengthdef draw(self):# 在这里直接实现绘制正方形的逻辑print(f"在坐标({self.x}, {self.y})处以边长{self.side_length}绘制正方形")# 客户端代码
if __name__ == "__main__":circle = CircleShape(1, 2, 3)circle.draw()square = SquareShape(4, 5, 6)square.draw()

在这个紧密耦合的示例中,绘制逻辑直接嵌入到了具体的抽象部分类中。这导致了以下问题:

  1. 如果需要修改绘制逻辑,例如更改绘制圆形的方式,就需要直接修改 CircleShape 类的代码,而不是通过更改实现部分来实现。
  2. 添加新的形状类,例如三角形,需要在每个具体形状类中重复实现绘制逻辑,造成代码重复。

因此,使用桥接模式可以将抽象部分和实现部分解耦,提高代码的灵活性和可扩展性。它允许我们在运行时选择不同的实现部分,而无需修改抽象部分的代码。

装饰器模式(Decorator)

在不修改原始对象的情况下动态地添加额外的功能。
装饰器模式通过将对象包装在一个具有相同接口的装饰器对象中来实现。

装饰器模式由以下几个关键角色组成:

  1. 抽象组件(Component):定义了被装饰对象和装饰器对象的公共接口。
  2. 具体组件(ConcreteComponent):实现了抽象组件接口,并定义了需要被装饰的对象。
  3. 抽象装饰器(Decorator):继承或实现了抽象组件接口,并持有一个对抽象组件的引用。它的主要目的是为了扩展或修改抽象组件的行为。
  4. 具体装饰器(ConcreteDecorator):实现了抽象装饰器接口,并扩展了抽象组件的行为。具体装饰器可以在调用被装饰对象的方法之前或之后添加额外的逻辑。
# 抽象组件
class Component:def operation(self):pass# 具体组件
class ConcreteComponent(Component):def operation(self):print("执行具体组件操作")# 抽象装饰器
class Decorator(Component):def __init__(self, component):self.component = componentdef operation(self):self.component.operation()# 具体装饰器
class ConcreteDecorator(Decorator):def operation(self):super().operation()self.additional_operation()def additional_operation(self):print("执行额外的操作")# 客户端代码
if __name__ == "__main__":# 创建具体组件component = ConcreteComponent()# 创建具体装饰器,并将具体组件作为参数传递decorator = ConcreteDecorator(component)# 执行操作decorator.operation()

在上述示例中,Component 是抽象组件,ConcreteComponent 是具体组件。Decorator 是抽象装饰器,ConcreteDecorator 是具体装饰器。客户端代码创建了一个具体组件对象,并将其作为参数传递给具体装饰器对象。装饰器对象在调用具体组件对象的方法之前或之后添加了额外的操作。

装饰器模式的优点是可以动态地添加功能,而不需要修改原始对象的代码。它提供了一种灵活的方式来扩展对象的行为,同时遵循开闭原则。然而,装饰器模式可能会导致类的数量增加,并且在多层装饰的情况下可能会变得复杂。因此,在使用时需要权衡利弊,并根据具体需求进行设计和实现。

组合模式(Composite)

将对象组合成树形结构以表示“部分-整体”的层次结构。
组合模式使得用户可以统一对待单个对象和组合对象,从而简化了代码的使用和维护。

组合模式由以下几个关键角色组成:

  1. 组件(Component):定义组合中对象的通用接口,可以是抽象类或接口。该接口声明了操作方法,例如添加、删除、获取子组件等。
  2. 叶子节点(Leaf):表示组合中的叶子对象,它没有子组件。实现组件接口的具体类。
  3. 组合节点(Composite):表示组合中的容器对象,它可以包含子组件。实现组件接口的具体类,并包含一个集合来存储子组件。
  4. 客户端(Client):通过组件接口操作组合对象。
# 组件接口
class Component:def add(self, component):passdef remove(self, component):passdef get_child(self, index):passdef operation(self):pass# 叶子节点
class Leaf(Component):def operation(self):print("执行叶子节点操作")# 组合节点
class Composite(Component):def __init__(self):self.children = []def add(self, component):self.children.append(component)def remove(self, component):self.children.remove(component)def get_child(self, index):return self.children[index]def operation(self):print("执行组合节点操作")for child in self.children:child.operation()# 客户端代码
if __name__ == "__main__":# 创建叶子节点leaf1 = Leaf()leaf2 = Leaf()# 创建组合节点,并添加叶子节点composite1 = Composite()composite1.add(leaf1)composite1.add(leaf2)# 创建叶子节点leaf3 = Leaf()# 创建组合节点,并添加叶子节点和组合节点composite2 = Composite()composite2.add(leaf3)composite2.add(composite1)# 执行操作composite2.operation()

在上述示例中,Component 是组件接口,Leaf 是叶子节点类,Composite 是组合节点类。客户端代码创建了一棵树形结构,包含了叶子节点和组合节点,并通过调用 operation() 方法执行操作。组合节点会递归调用其子组件的操作方法,从而实现整个树形结构的操作。

组合模式的优点是简化了代码的使用和维护,使得客户端可以统一对待单个对象和组合对象。它也提供了灵活性和可扩展性,因为可以轻松地添加新的叶子节点或组合节点。然而,组合模式可能会增加系统的复杂性,并且在某些情况下可能会导致性能问题,因此在使用时需要权衡利弊。

外观模式(Facade)

提供了一个统一的接口,用于访问子系统中的一组接口。
外观模式隐藏了子系统的复杂性,为客户端提供了一个简单的接口来与子系统进行交互。

下面是一个外观模式的示例代码:

# 子系统类A
class SubsystemA:def operation_a(self):print("执行子系统A的操作")# 子系统类B
class SubsystemB:def operation_b(self):print("执行子系统B的操作")# 子系统类C
class SubsystemC:def operation_c(self):print("执行子系统C的操作")# 外观类
class Facade:def __init__(self):self.subsystem_a = SubsystemA()self.subsystem_b = SubsystemB()self.subsystem_c = SubsystemC()def operation(self):self.subsystem_a.operation_a()self.subsystem_b.operation_b()self.subsystem_c.operation_c()# 客户端代码
facade = Facade()
facade.operation()

在上述代码中,有三个子系统类 SubsystemASubsystemBSubsystemC,它们分别提供了各自的操作。外观类 Facade 将这些子系统进行了封装,并提供了一个统一的接口 operation()

在客户端代码中,我们创建了一个外观对象 facade,然后通过调用 facade.operation() 来访问子系统的操作。在内部,外观对象会依次调用子系统类的相应方法,隐藏了子系统的复杂性。

当我们执行 facade.operation() 时,会依次输出:

执行子系统A的操作
执行子系统B的操作
执行子系统C的操作

这样,客户端就可以通过简单的接口来访问子系统的功能,而不需要了解和处理子系统的复杂逻辑。外观模式帮助简化了系统的使用和维护。

享元模式(Flyweight)

通过共享对象来减少内存使用和提高性能。
享元模式适用于存在大量相似对象的场景,通过共享这些对象的内部状态,来减少对象的数量。

下面是一个在模具设计中使用享元模式的示例代码:

# 模具接口
class Mold:def __init__(self, mold_type):self.mold_type = mold_typedef produce(self, product):print(f"生产 {product} 使用 {self.mold_type} 模具")# 享元工厂类
class MoldFactory:def __init__(self):self.molds = {}def get_mold(self, mold_type):if mold_type not in self.molds:self.molds[mold_type] = Mold(mold_type)return self.molds[mold_type]# 客户端代码
mold_factory = MoldFactory()# 第一批产品使用 A 类型模具
mold_a = mold_factory.get_mold("A")
mold_a.produce("产品1")
mold_a.produce("产品2")# 第二批产品使用 B 类型模具
mold_b = mold_factory.get_mold("B")
mold_b.produce("产品3")
mold_b.produce("产品4")# 第三批产品继续使用 A 类型模具
mold_a.produce("产品5")
mold_a.produce("产品6")

在上述代码中,Mold 类表示模具,每个模具有一个类型(mold_type)属性,并且可以生产产品。MoldFactory 类是享元工厂类,用于创建和管理模具对象。

在客户端代码中,我们通过 MoldFactory 获取模具对象。如果请求的模具对象已经存在于享元工厂中,则直接返回该对象;否则,创建一个新的模具对象并添加到享元工厂中。这样,相同类型的模具对象会被共享使用。

在示例中,我们先获取了一个类型为 “A” 的模具对象,并使用它生产了两个产品。然后,我们获取了一个类型为 “B” 的模具对象,并使用它生产了两个产品。最后,我们又使用之前获取的 “A” 类型模具对象生产了两个产品。

运行上述代码,会输出以下结果:

生产 产品1 使用 A 模具
生产 产品2 使用 A 模具
生产 产品3 使用 B 模具
生产 产品4 使用 B 模具
生产 产品5 使用 A 模具
生产 产品6 使用 A 模具

可以看到,尽管我们只创建了两个不同类型的模具对象,但通过共享对象的方式,成功地减少了模具对象的数量。享元模式帮助我们节省了内存开销,并提高了系统的性能。

代理模式(Proxy)

它提供了一个代理对象,控制对另一个对象的访问。
通过使用代理对象,可以在访问对象时添加额外的逻辑,例如权限控制、缓存、延迟加载等。

下面是一个代理模式的示例代码:

# 主题接口
class Subject:def request(self):pass# 真实主题类
class RealSubject(Subject):def request(self):print("处理真实请求")# 代理类
class Proxy(Subject):def __init__(self, real_subject):self.real_subject = real_subjectdef request(self):# 在访问真实主题前添加额外的逻辑self.pre_request()# 调用真实主题的请求方法self.real_subject.request()# 在访问真实主题后添加额外的逻辑self.post_request()def pre_request(self):print("代理对象处理请求前的操作")def post_request(self):print("代理对象处理请求后的操作")# 客户端代码
real_subject = RealSubject()
proxy = Proxy(real_subject)# 通过代理对象访问真实主题
proxy.request()

在上述代码中,有一个主题接口 Subject,其中定义了一个 request() 方法。RealSubject 类是真实主题类,实现了主题接口,并提供了具体的请求处理逻辑。

Proxy 类是代理类,也实现了主题接口,并持有一个真实主题对象。在代理类的 request() 方法中,可以在调用真实主题对象的请求方法前后添加额外的逻辑。在示例中,我们在访问真实主题前后分别输出了一些操作。

在客户端代码中,我们创建了一个真实主题对象 real_subject,然后将其传递给代理对象 proxy 的构造函数。通过代理对象 proxy,我们可以访问真实主题的请求方法。

当执行 proxy.request() 时,会依次输出:

代理对象处理请求前的操作
处理真实请求
代理对象处理请求后的操作

可以看到,代理对象在访问真实主题前后添加了额外的操作。代理模式帮助我们控制对真实主题的访问,并可以在访问前后进行一些附加处理。这样,我们可以在不修改真实主题代码的情况下,对其进行扩展和增强。

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

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

相关文章

有向无权图的最短路径

在运筹学领域的经典模型中,最大流问题、多商品网络流问题和最短路径问题等都依附在图上对问题进行描述,同样,当我们梳理问题的数学模型,或理解相关问题的求解算法时,也要依靠它。因此,我将总结和图相关的问…

Mac笔记本打开Outlook提示:您需要最新的版本的Outlook才能使用此数据库

Mac笔记本打开Outlook提示:您需要最新的版本的Outlook才能使用此数据库 故障现象: 卸载旧的office安装新版的office,打开outlook提示:您需要最新的版本的outlook才能使用此数据库。 故障截图: 故障原因:…

让文字在盒子中水平居中与垂直居中

简单方法&#xff1a; 1.先用text-align: center;将文字垂直居中。 2.再用line-height: Xpx;将元素的行高设置为与父元素同样的高度。&#xff08;这里的X代表父元素的高度&#xff09; 举例&#xff1a; 对于该网页的代码如下&#xff1a; <!DOCTYPE html> <html&…

AI中文版怎么用,版本分享,GPT官网入口

网页版上线啦&#xff0c;在线助力大学生、上班族的高效生活&#xff01; GPT4.0是OpenAI最新推出的聊天模型&#xff0c;它的语言理解和生成能力比以前的版本更强大。对于忙碌的上班族来说&#xff0c;GPT4.0能帮助你高效处理工作中的大部分写作任务&#xff0c;比如撰写报告…

Docker与VM虚拟机的区别以及Docker的特点

01、本质上的区别 VM(VMware)在宿主机器、宿主机器操作系统的基础上创建虚拟层、虚拟化的操作系统、虚拟化的仓库&#xff0c;然后再安装应用&#xff1b; Container(Docker容器)&#xff0c;在宿主机器、宿主机器操作系统上创建Docker引擎&#xff0c;在引擎的基础上再安装应…

【MySQL】索引和事务(B树、B+树图解原理)

一、索引 1.1 什么是索引&#xff1f; 索引是一种特殊的文件&#xff0c;包含着对数据表里所有记录的引用指针。可以对表中的一列或多列创建索引&#xff0c;并指定索引的类型&#xff0c;各类索引有各自的数据结构实现。 1.2 索引的作用 &#x1f693;&#xff08;1&#…

Android修行手册 - 阴影效果的几种实现以及一些特别注意点

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列点击跳转>ChatGPT和AIGC &#x1f449;关于作者 专…

m1 rvm install 3.0.0 Error running ‘__rvm_make -j8‘

在使用M1 在安装cocopods 前时&#xff0c;安装 rvm install 3.0.0遇到 rvm install 3.0.0 Error running __rvm_make -j8 备注: 该图片是借用其他博客图片&#xff0c;因为我的环境解决完没有保留之前错误信息。 解决方法如下&#xff1a; 1. brew uninstall --ignore-depe…

TCP协议通讯流程

文章目录&#xff1a; 通讯流程全过程浏览建立连接过程数据传输过程断开连接问题 通讯流程全过程浏览 下图是基于TCP协议的客户端/服务器程序的一般流程&#xff1a; 上图就是TCP协议的通信流程&#xff0c;接下来认识初步认识以下TCP建立连接&#xff08;三次握手&#xff0…

vue实现上传文件到七牛云

第一步&#xff1a;安装插件 npm install qiniu-js 第二步&#xff0c;页面中引入插件 import * as qiniu from qiniu-js 第三步&#xff0c;调用方法 // 必须手动上传文件async onChangeFile() {let that this;let file this.$refs.uploadfile.files[0];if (file) {let …

Notion汉化

Notion真无语&#xff0c;汉化版都没有。真的无力吐槽。 2023.11.7汉化经历 教程链接&#xff1a;github Reamd7/notion-zh_CN at 2.4.20-handmade (github.com) 网页版&#xff1a; 油猴下载插件。 Notion中文汉化 浏览器插件下载 windows&#xff1a; github realse 这…

【带头学C++】----- 六、结构体 ---- 6.7 共用体以及枚举类型

6.7 共用体以及枚举类型 结构体:结构体用于组合不同类型的数据&#xff0c;每个字段占用独立的内存空间。 共用体:共用体也组合不同类型的数据&#xff0c;但所有字段共享同一块内存。 因此&#xff0c;结构体适合表示具有多个属性的对象&#xff0c;而共用体适合表示可以具…

力扣labuladong——一刷day32

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、力扣654. 最大二叉树二、力扣105. 从前序与中序遍历序列构造二叉树三、力扣106. 从中序与后序遍历序列构造二叉树四、力扣889. 根据前序和后序遍历构造二叉…

Linux 命令:lsof(列出打开的文件)

1. 写在前面 本文主要介绍&#xff1a;Linux “lsof” 命令&#xff1b; 公众号&#xff1a; 滑翔的纸飞机 2. lsof 命令 lsof 命令是 "List Open Files&#xff08;列出打开的文件&#xff09;"的缩写&#xff0c;用于显示打开的文件以及使用这些文件的进程。 在…

vite 样式按需加载

用于按需引入组件库样式的插件。 vite-plugin-impvite-plugin-style-import 以上两个插件可以实现按需引入组件库样式&#xff0c;尝试后发现vite-plugin-imp这个插件目前有个问题是&#xff0c;它支持按照组件动态引入组件内部的样式&#xff0c;但是antd还定义了一些全局样…

拜耳阵列(Bayer Pattern)以及常见彩色滤波矩阵(CFA)

一、拜耳阵列的来源 图像传感器将光线转化成电流&#xff0c;光线越亮&#xff0c;电流的数值就越大&#xff1b;光线越暗&#xff0c;电流的数值就越小。图像传感器只能感受光的强弱&#xff0c;无法感受光的波长。由于光的颜色由波长决定&#xff0c;所以图像传播器无法记录…

【JUC】八、阻塞队列

文章目录 1、阻塞队列概述2、阻塞队列分类3、 阻塞队列的四组核心方法4、Demo 队列&#xff0c;先进先出&#xff0c;类似排队栈&#xff0c;先进后出&#xff0c;用于要优先处理最近发生的事件的场景 1、阻塞队列概述 阻塞队列&#xff0c;一个生产消费模式&#xff0c;当&a…

LCD智能婴幼儿秤pcba方案

LCD智能婴幼儿秤采用SIC8833芯片开发设计&#xff0c;内置程序搭载称重算法&#xff0c;配合高精度传感器&#xff0c;能够准确测量出宝宝体重。并且秤台有身高标识&#xff0c;能够测量幼儿的身高。通过蓝牙上传云端APP&#xff0c;实时记录幼儿状态。 一、LCD智能婴幼儿秤方案…

【机器学习8】采样

1 均匀分布随机数 均匀分布是指整个样本空间中的每一个样本点对应的概率&#xff08;密度&#xff09; 都是相等的。 根据样本空间是否连续&#xff0c; 又分为离散均匀分布和连续均匀分布。编程实现均匀分布随机数生成器一般可采用线性同余法&#xff08;Linear Congruential…

防爆五参数气象仪的科技力量

WX-FBQ2 随着科技的不断进步&#xff0c;气象监测设备也在不断升级和完善。 防爆五参数气象仪是一种可以同时监测温度、湿度、压力、风速和风向五个基本气象参数的仪器。它采用了气象监测技术&#xff0c;不仅可以实时监测气象数据&#xff0c;还可以对数据进行分析和处理。 …