【设计模式】深入理解Python中的桥接模式(Bridge Pattern)
深入理解Python中的桥接模式(Bridge Pattern)
在软件开发中,我们常常会遇到一个类随着功能的扩展,继承层次越来越复杂,导致系统僵化,难以维护。桥接模式(Bridge Pattern)提供了一种优雅的方式,通过分离抽象部分和实现部分,降低类的复杂性,使得系统具有更好的扩展性和灵活性。
本文将详细介绍桥接模式的原理、适用场景、如何在Python中实现它,以及一些常见的优化方式。
1. 什么是桥接模式?
桥接模式是一种结构型设计模式,旨在将抽象部分与它的实现部分分离,使它们可以独立地进行变化。简单来说,桥接模式通过创建独立的抽象层和实现层,让它们分别可以独立扩展,不互相影响。
这种模式的关键在于将一个大类拆分成多个更小的类,并通过“桥接”让这些类协同工作,从而减少子类的数量并避免层次过于复杂。
桥接模式的结构
桥接模式包含两个主要部分:
- 抽象部分(Abstraction):定义了高层的操作接口,内部持有对实现部分(Implementor)的引用。
- 实现部分(Implementor):定义底层实现接口,提供抽象部分依赖的实际功能。
通过桥接模式,可以将抽象和实现分离,使得它们可以独立扩展和演化。
UML类图表示
+------------------+ +-------------------+
| Abstraction | | Implementor |
+------------------+ +-------------------+
| - implementor | | +operation_impl() |
| +operation() | +-------------------+
+------------------+ ▲▲ || || |
+------------------+ +-------------------+
| RefinedAbstraction| | ConcreteImplementor|
+------------------+ +-------------------+
| +operation() | | +operation_impl() |
+------------------+ +-------------------+
- Abstraction:定义抽象操作接口,内部引用
Implementor
接口。 - Refined Abstraction:扩展了
Abstraction
的具体操作实现。 - Implementor:定义底层操作接口,通常有多个不同的实现类。
- ConcreteImplementor:具体实现
Implementor
接口,提供实际的业务逻辑。
2. 桥接模式的应用场景
桥接模式在以下场景非常有用:
- 避免类爆炸:当类的属性和行为多种组合时,通过继承会导致子类过多,桥接模式可以减少类的数量。
- 多维度变化的系统:当一个系统可能存在多个维度的变化(如操作系统平台和UI风格),桥接模式可以将这些变化独立出来,并允许它们自由组合。
- 分离抽象和实现:在需要抽象和实现解耦的系统中,桥接模式提供了一种优雅的方式来降低系统耦合度。
典型应用场景
- 跨平台GUI工具:UI控件可能需要支持多个平台(如Windows、Linux、macOS),同时还可能有不同的样式和行为。桥接模式可以将平台依赖和控件行为分开实现。
- 文件系统操作:文件系统的抽象和具体操作方式(如不同操作系统下的文件系统实现)可以通过桥接模式分离,使得系统支持多种文件操作方式。
3. Python 实现桥接模式
3.1 定义实现者接口(Implementor)
首先,我们定义一个 Implementor
接口,它代表系统的实际功能实现部分。在本例中,假设我们要实现不同的绘图工具(比如画笔、喷枪),每个工具的操作不同,但它们都有一个绘制功能。
from abc import ABC, abstractmethod# 实现者接口
class DrawingImplementor(ABC):@abstractmethoddef draw_shape(self, shape: str):pass
3.2 实现具体实现者(Concrete Implementor)
接下来,定义两个具体的实现者,一个是使用画笔绘图,另一个是使用喷枪绘图。
# 具体实现者:画笔
class Pen(DrawingImplementor):def draw_shape(self, shape: str):return f"Drawing {shape} with a Pen."# 具体实现者:喷枪
class SprayGun(DrawingImplementor):def draw_shape(self, shape: str):return f"Drawing {shape} with a Spray Gun."
3.3 定义抽象类(Abstraction)
Abstraction
定义了系统的高层操作接口,持有 Implementor
的引用,客户端可以通过该接口调用底层的实现逻辑。
# 抽象类
class Shape(ABC):def __init__(self, implementor: DrawingImplementor):self.implementor = implementor@abstractmethoddef draw(self):pass
3.4 实现具体抽象类(Refined Abstraction)
我们需要定义具体的形状类,它们继承 Shape
,并调用 Implementor
进行具体的绘图操作。比如,我们可以定义 Circle
和 Square
两个形状,它们可以使用不同的工具来绘制。
# 具体的抽象类:圆形
class Circle(Shape):def draw(self):return self.implementor.draw_shape("Circle")# 具体的抽象类:正方形
class Square(Shape):def draw(self):return self.implementor.draw_shape("Square")
3.5 客户端代码
客户端通过创建具体的抽象类,并传入不同的实现者,来进行具体的操作。可以通过桥接模式轻松切换实现者,而不需要修改高层逻辑。
# 测试桥接模式
pen = Pen()
spray_gun = SprayGun()circle_with_pen = Circle(pen)
print(circle_with_pen.draw()) # 使用画笔画圆形square_with_spray_gun = Square(spray_gun)
print(square_with_spray_gun.draw()) # 使用喷枪画正方形
输出结果:
Drawing Circle with a Pen.
Drawing Square with a Spray Gun.
通过这个例子,我们可以看到,Circle
和 Square
形状的高层逻辑与具体的绘制工具解耦,绘制工具可以根据需要灵活切换而不影响其他代码。
4. 桥接模式的优缺点
优点
- 分离抽象和实现:桥接模式将抽象层和实现层分离,降低了代码耦合度,增强了系统的可扩展性。
- 提高系统的可维护性:通过减少子类的数量,桥接模式避免了复杂的继承结构,使得系统更加易于维护。
- 扩展性强:抽象部分和实现部分都可以独立扩展,不会相互影响。新增抽象类或实现类时不需要修改已有代码,符合开闭原则。
缺点
- 增加复杂性:虽然桥接模式降低了子类的数量,但它引入了更多的接口和类,可能会增加系统的复杂度,特别是当系统规模较小时,过度设计反而使代码难以理解。
- 难以理解:对于初学者来说,桥接模式可能不容易理解,特别是分离抽象和实现的概念在某些简单场景中显得多余。
5. 改进桥接模式:使用Python的动态特性
在Python中,由于其动态特性,我们可以进一步简化桥接模式的实现。例如,直接将实现逻辑作为参数传递给抽象类,而无需定义严格的接口,这可以减少代码的冗余。
动态实现示例
class Shape:def __init__(self, drawing_tool):self.drawing_tool = drawing_tooldef draw(self, shape: str):return self.drawing_tool(shape)# 使用画笔绘制
def draw_with_pen(shape):return f"Drawing {shape} with a Pen."# 使用喷枪绘制
def draw_with_spray_gun(shape):return f"Drawing {shape} with a Spray Gun."# 测试动态桥接
circle = Shape(draw_with_pen)
print(circle.draw("Circle"))square = Shape(draw_with_spray_gun)
print(square.draw("Square"))
输出结果与之前相同,但我们通过Python的动态特性简化了代码结构,不需要显式定义实现者接口。
这个改进版本适用于较为简单的场景。
6. 结论
桥接模式是一种非常强大的设计模式,尤其适合在多维度变化的系统中应用。它通过分离抽象和实现,降低了代码的耦合度,增强了系统的扩展性和维护性。在Python中,桥接模式不仅能帮助我们避免复杂的继承结构,还能通过语言的动态特性进行简化,使得代码更加灵活。
在本文中,我们讨论了桥接模式的核心概念、适用场景、详细的Python实现以及优化方式,希望你在未来的项目中能够灵活运用这一模式。
相关文章:
【设计模式】深入理解Python中的桥接模式(Bridge Pattern)
深入理解Python中的桥接模式(Bridge Pattern) 在软件开发中,我们常常会遇到一个类随着功能的扩展,继承层次越来越复杂,导致系统僵化,难以维护。桥接模式(Bridge Pattern)提供了一种…...
YOLOv11改进策略【卷积层】| SAConv 可切换的空洞卷积 二次创新C3k2
一、本文介绍 本文记录的是利用SAConv优化YOLOv11的目标检测网络模型。空洞卷积是一种在不增加参数量和计算量的情况下,通过在卷积核元素之间插入空洞来扩大滤波器视野的技术。并且为了使模型能够适应不同尺度的目标,本文利用SAConv将不同空洞率卷积结果进行结合,来获取更全…...
Javaweb基础-axios
Axios 是一个基于 Promise 的 HTTP 库,可以用在浏览器和 node.js 中。 GET方法 get请求第一种写法 //后端 Slf4j RestController RequestMapping("/demo") public class DemoController {RequestMapping("/getTest")// 被RequestParam标记的参数…...
智能EDA小白从0开始 —— DAY20 OrCAD
以下是对OrCAD和MATLAB两种EDA工具的深入解析,内容扩展至约2220字: OrCAD:电子设计自动化的强大工具 OrCAD,作为电子设计自动化(EDA)领域的佼佼者,为电子工程师们提供了一套全面的设计解决方案…...

C# WebApi 接口测试工具:WebApiTestClient应用技术详解
目录 一、引言 二、WebApiTestClient介绍 1、特性 2、应用场景 三、WebApiTestClient具体使用 1、WebApi项目引入组件 2、如何使用组件 1、修改Api.cshtml文件 2、配置读取注释的xml路径 3、测试接口 四、总结 一、引言 由于最近项目需要开发WebApi接口&…...
Qt_ymode自己实现
文章内容: 通过Qt实现Ymode协议的封装。通过传入的数据从里面一包一包拿数据。可以用作平时串口和网口的通信。也可以用来程序升级。 #include "ymodem.h"Ymodem::Ymodem() {m_data = nullptr; }Ymodem...

5.3章节python中字典:字典创建、元素访问、相关操作
1.字典的创建和删除 2.字典的访问和遍历 3.字典的相关操作 4.字典的生成式 一、字典的创建和删除 字典(dictionary)是一种用于存储键值对(key-value pairs)的数据结构。每个键(key)都映射到一个值…...

ECCV2024 Tracking 汇总
一、OneTrack: Demystifying the Conflict Between Detection and Tracking in End-to-End 3D Trackers paper: https://www.ecva.net/papers/eccv_2024/papers_ECCV/papers/01174.pdf 二、VETRA: A Dataset for Vehicle Tracking in Aerial Imagery paper&#…...
C语言知识点
命名规则: 字符组成:标识符只能由字母(A~Z,a~z)、数字(0~9)和下划线(_)组成。首字符要求:标识符的第一个字符必须是字母或下划线,不能是数字。长…...
ICMP协议以及ARP欺骗攻击
ping 命令使用的是 ICMP(Internet Control Message Protocol)协议,而不是 TCP 或 UDP 协议。因此,ping 命令并不使用特定的端口号。 ICMP 协议 ICMP 是一种网络层协议,主要用于在 IP 网络中传递控制消息。ping 命令利…...
qt5.12.12插件机制无法加载插件问题
环境:win11 vs2015 qt5.12.12 问题描述:确保插件代码正确的情况下,无法解析插件接口(即QPluginLoader类的instance(); 返回为空)。 问题现象:1、qt5.12.12的debug下无法解析;2、release下禁…...

机器学习面试笔试知识点-线性回归、逻辑回归(Logistics Regression)和支持向量机(SVM)
机器学习面试笔试知识点-线性回归、逻辑回归Logistics Regression和支持向量机SVM 一、线性回归1.线性回归的假设函数2.线性回归的损失函数(Loss Function)两者区别3.简述岭回归与Lasso回归以及使用场景4.什么场景下用L1、L2正则化5.什么是ElasticNet回归6.ElasticNet回归的使…...

SpringBoot民宿预订系统设计与实现
2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统,它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等,非常…...

linux环境下C程序的编译过程以及makefile的简单使用
在windows下,很多用来进行编程软件对于写好的文件,点击编译即可生成想要文件。如.exe可执行文件,.hex文件或者.bin文件等等。软件为我们省略了很多事。但是对于linux初学者来说,初次接触linux系统,面对命令行黑框框有点…...

【从零开始的LeetCode-算法】945. 使数组唯一的最小增量
给你一个整数数组 nums 。每次 move 操作将会选择任意一个满足 0 < i < nums.length 的下标 i,并将 nums[i] 递增 1。 返回使 nums 中的每个值都变成唯一的所需要的最少操作次数。 生成的测试用例保证答案在 32 位整数范围内。 示例 1: 输入&am…...

Java程序设计:spring boot(2)
目录 1 Spring MVC 零配置创建与部署 1.1 创建Spring MVC Web⼯程 1.2 pom.xml 添加坐标相关配置 1.3 添加源代码 1.4 添加视图 1.5 SpringMVC 配置类添加 1.6 入口文件代码添加 1.7 部署与测试 2 Spring Boot 概念&特点 2.1 框架概念 2.2 框架特点 2.3 Spring…...

服务器运维监控平台
云监控平台-简介 一:简介 “phoenix” 是一个灵活可配置的开源监控平台,主要用于监控应用程序、服务器、docker、数据库、网络、tcp 端口和 http 接口,通过实时收集、汇聚和分析监控信息,实现在发现异常时立刻推送告警信息&…...
css中 global 和 deep(两个样式穿透) 区别
1.:global(selector):这个伪类选择器会选择所有全局的、未被其他样式表覆盖的元素。换句话说,它会匹配所有没有被其他样式表(例如内联样式或外部样式表)所影响的元素。 :global(p) {color: red; }这段代码会将所有 <p> 元素…...

【星闪技术】WS63E模块的WiFi客户端测试
引言 我所计划的WS63E测试要实现MQTT联网,所以首先需要确保开发板连接WiFi。今天来测试一下WiFi功能。 程序分析 WiFi客户端的例子在src/application/samples/wifi/sta_sample目录下。这个例子看上去和hi3861的例子差不多。 这段程序是一个用于嵌入式设备的Wi-F…...

Android面试之5个Kotlin深度面试题:协程、密封类和高阶函数
本文首发于公众号“AntDream”,欢迎微信搜索“AntDream”或扫描文章底部二维码关注,和我一起每天进步一点点 面试题目1:Kotlin中的协程与线程的区别是什么?如何在Android中使用协程进行异步编程? 解答: 协…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...

Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...
Spring Boot 实现流式响应(兼容 2.7.x)
在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate 缓存机制会…...

React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...

2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...

《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...

相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...