设计模式(结构型)-桥接模式

目录

摘要

定义

类图

角色

具体实现

优缺点

优点

缺点

使用场景

使用案例

JDBC 和桥接模式

总结


摘要

        在软件开发领域,随着系统规模和复杂性的不断攀升,如何设计出具有良好扩展性、灵活性以及可维护性的软件架构成为关键挑战。桥接模式作为一种重要的结构型设计模式,为解决这些问题提供了有效的方案。它通过巧妙的设计,将抽象部分与实现部分进行分离,使二者能够独立发展变化,极大地提升了软件系统应对变化的能力。

定义

        将抽象部分与它的实现部分分离,使它们都可以独立地变化。在许多传统设计中,抽象与实现紧密耦合,一旦其中一方发生改变,往往会对另一方产生较大影响,这无疑增加了系统的维护成本和复杂性。桥接模式则打破了这种束缚,通过聚合关系代替继承关系,实现了抽象化和实现化部分的解耦。​

        例如,在一个图形绘制系统中,假设我们有不同类型的图形(如圆形、矩形等),同时需要在不同的平台(如 Windows、Mac 等)上进行绘制。若采用传统的继承方式,每一种图形类型都需要为每个平台创建一个子类,这样会导致类的数量呈指数级增长,代码的维护和扩展变得极为困难。而桥接模式可以将图形的抽象(如 Shape 类)与具体的绘制实现(如 WindowsDrawingAPI、MacDrawingAPI 等实现类)分离,通过聚合关系将它们关联起来,使得图形类型和绘制平台能够独立发展,大大简化了系统的设计。

类图

角色

  • Abstraction(抽象类):处于整个结构的上层,用于定义抽象的接口。它一般是抽象类而非接口,其中定义了一个 Implementor(实现类接口)类型的对象并维护该对象,通过这种方式与 Implementor 建立关联关系。抽象类中声明的方法往往依赖于 Implementor 中定义的操作来实现具体功能。​

  • RefinedAbstraction(提炼抽象类):对 Abstraction 定义的接口进行扩充。通常情况下,它不再是抽象类而是具体类,实现了在 Abstraction 中声明的抽象业务方法。在其实现过程中,会调用在 Implementor 中定义的业务方法,从而将抽象与实现进行结合。​

  • Implementor(实现类接口):该接口定义了实现类的基本操作规范。需要注意的是,它的接口设计不一定要与 Abstraction 的接口完全一致,事实上二者可能差异较大。Implementor 接口主要聚焦于提供一些基础的、底层的操作,这些操作的具体实现由其子类负责。通过这种抽象接口的定义,为不同的具体实现提供了统一的规范。​

  • ConcreteImplementor(具体实现类):具体实现 Implementor 接口的类,针对不同的业务需求和场景,在各个 ConcreteImplementor 中提供基本操作的不同实现。在程序运行过程中,ConcreteImplementor 对象会替换其父类对象,为抽象类提供具体的业务操作方法,实现实际的功能。

具体实现

  • Abstraction(抽象类):作为抽象层的代表,它为整个系统提供了一个高层次的抽象接口。以图形绘制系统为例,抽象类 Shape 可以定义一些通用的图形操作方法,如绘制(draw)、缩放(resize)等,但并不涉及具体的绘制逻辑。它持有一个实现类接口 IDrawingAPI 类型的对象,通过该对象来调用具体的绘制实现。

public abstract class Shape {​protected IDrawingAPI drawingAPI;​public Shape(IDrawingAPI drawingAPI) {​this.drawingAPI = drawingAPI;​}​public abstract void draw();​public abstract void resize(double factor);​
}
  • RefinedAbstraction(提炼抽象类):以 Circle 类为例,它继承自 Shape 抽象类,是提炼抽象类的具体体现。在 Circle 类中,实现了 Shape 类中定义的 draw 和 resize 方法,并借助 drawingAPI 对象调用具体的绘制实现。

public class Circle extends Shape {​private double x, y, radius;​public Circle(double x, double y, double radius, IDrawingAPI drawingAPI) {​super(drawingAPI);​this.x = x;​this.y = y;​this.radius = radius;​}​@Override​public void draw() {​drawingAPI.drawCircle(x, y, radius);​}​@Override​public void resize(double factor) {​radius *= factor;​}​
}
  • Implementor(实现类接口):定义了具体实现类需要遵循的接口规范。在图形绘制系统中,IDrawingAPI 接口定义了绘制圆形、矩形等图形的方法。

public interface IDrawingAPI {​void drawCircle(double x, double y, double radius);​void drawRectangle(double x1, double y1, double x2, double y2);​
}
  • ConcreteImplementor(具体实现类):如 WindowsAPI 和 MacAPI 类,分别实现了 IDrawingAPI 接口,提供了在 Windows 和 Mac 平台上的具体绘制实现。

public class WindowsAPI implements IDrawingAPI {​@Override​public void drawCircle(double x, double y, double radius) {​System.out.println("在Windows平台绘制圆形,圆心:(" + x + ", " + y + "),半径:" + radius);​}​@Override​public void drawRectangle(double x1, double y1, double x2, double y2) {​System.out.println("在Windows平台绘制矩形,左上角:(" + x1 + ", " + y1 + "),右下角:(" + x2 + ", " + y2 + ")");​}​
}​
public class MacAPI implements IDrawingAPI {​@Override​public void drawCircle(double x, double y, double radius) {​System.out.println("在Mac平台绘制圆形,圆心:(" + x + ", " + y + "),半径:" + radius);​}​@Override​public void drawRectangle(double x1, double y1, double x2, double y2) {​System.out.println("在Mac平台绘制矩形,左上角:(" + x1 + ", " + y1 + "),右下角:(" + x2 + ", " + y2 + ")");​}​
}

优缺点

优点

  • 实现抽象和实现的分离:这是桥接模式的核心优势。通过将抽象部分与实现部分解耦,使得它们能够独立地进行演化。例如,在图形绘制系统中,图形类型的扩展(如新增三角形、菱形等图形)不会影响到绘制平台的实现,反之,绘制平台的更新(如支持新的操作系统)也不会对图形类型造成影响,大大提高了系统的灵活性和可维护性。​

  • 提高系统的可扩充性:在桥接模式下,两个变化维度(抽象和实现)中的任意一个进行拓展,都无需对原有系统进行大规模修改。以消息发送系统为例,若要新增一种消息类型(如推送消息),只需在抽象部分添加相应的类,并在实现部分关联已有的消息发送方式(如邮件、短信等);若要新增一种消息发送方式(如即时通讯工具),也只需在实现部分添加具体实现类,然后在需要使用的抽象类中进行关联即可,符合开闭原则。​

  • 避免多层继承的弊端:多层继承方案容易违背类的单一职责原则,导致代码复用性差,并且类的数量会随着继承层次的增加而迅速膨胀,使得系统变得复杂难以维护。桥接模式采用聚合关系替代继承关系,有效避免了这些问题,使得系统结构更加简洁清晰。

缺点

  • 增加系统的理解与设计难度:由于桥接模式中的聚合关联关系建立在抽象层,这要求开发者在设计和编程时,需要从更高的抽象层次去思考和规划,对开发者的抽象思维能力和设计经验有较高要求。初学者可能较难理解和掌握这种设计方式,增加了学习和上手的难度。​

  • 使用范围具有一定的局限性:桥接模式的应用依赖于正确识别出系统中两个独立变化的维度。然而,在一些复杂系统中,准确判断和分离这两个维度并非易事。如果对系统的分析不准确,强行使用桥接模式,可能不仅无法发挥其优势,反而会使系统变得更加复杂混乱,因此其使用场景受到一定限制。

使用场景

  • 一个类存在两个独立变化的维度,且这两个维度都需要进行拓展:例如在电商系统中,商品有不同的类别(如电子产品、服装、食品等),同时有不同的促销策略(如打折、满减、赠品等)。类别和促销策略这两个维度相互独立且都可能不断扩展,使用桥接模式可以将商品类别抽象与促销策略实现进行分离,方便系统根据业务发展进行功能扩展。​

  • 如果一个系统需要在构建的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承关系:以跨平台的游戏开发为例,游戏中的角色具有各种行为(如移动、攻击、防御等抽象行为),而这些行为在不同的游戏平台(如 PC、手机、主机等)上有不同的实现方式。通过桥接模式,可以将角色行为的抽象与平台相关的实现解耦,使游戏在不同平台上的移植和扩展更加容易,提高了系统的灵活性。​

  • 对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统:如在图形编辑软件中,图形元素有多种类型(矩形、圆形、多边形等),并且需要支持不同的显示模式(普通模式、高清模式、3D 模式等)。若采用继承方式,会产生大量的子类(每种图形类型对应每种显示模式都需要一个子类)。使用桥接模式,将图形类型抽象与显示模式实现分离,可以有效避免类的数量爆炸,简化系统设计。

使用案例

JDBC 和桥接模式

        JDBC(Java Database Connectivity)是 Java 语言中用于连接和操作数据库的标准 API,它很好地体现了桥接模式的应用。在 JDBC 中,抽象部分是 Java.sql 包中定义的一系列接口,如 Connection、Statement、ResultSet 等,这些接口定义了与数据库交互的抽象操作,如建立连接、执行 SQL 语句、获取查询结果等。而实现部分则是各个数据库厂商提供的数据库驱动,如 MySQL 的驱动、Oracle 的驱动等。不同的数据库驱动实现了 Java.sql 包中的接口,提供了针对各自数据库的具体操作实现。​

        通过这种方式,Java 程序在使用 JDBC 操作数据库时,无需关心具体使用的是哪种数据库以及其底层实现细节,只需要通过抽象接口进行操作。当需要更换数据库时,只需要更换对应的数据库驱动(实现部分),而 Java 程序中使用 JDBC 接口的代码(抽象部分)无需修改,极大地提高了代码的可移植性和可维护性,充分发挥了桥接模式将抽象与实现分离的优势。​

总结

        桥接模式作为一种强大的结构型设计模式,通过独特的设计理念和结构,有效地解决了软件系统中抽象与实现耦合的问题,为构建灵活、可扩展的软件架构提供了有力的支持。虽然它存在一定的应用门槛和局限性,但在合适的场景下应用,能够显著提升软件系统的质量和开发效率,是开发者在软件设计过程中不可或缺的重要工具。

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

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

相关文章

Go 微服务框架 | 中间件

文章目录 定义中间件前置中间件后置中间件路由级别中间件 定义中间件 中间件的作用是给应用添加一些额外的功能,但是不会影响原有应用的编码方式,想用的时候直接添加,不想用的时候也可以轻松去除,实现所谓的可插拔。中间件的实现…

leetcode 198. House Robber

本题是动态规划问题。 第一步,明确并理解dp数组以及下标的含义 dp[i]表示从第0号房间一直到第i号房间(包含第i号房间)可以偷到的最大金额,具体怎么偷这里不考虑,第i1号及之后的房间也不考虑。换句话说,dp[i]也就是只考虑[0,i]号…

掌趣科技前端面试题及参考答案

你使用 Vue 的频率如何,用得比较多吗? 在前端开发工作中,我对 Vue 的使用较为频繁。Vue 作为一款轻量级、易于上手且功能强大的前端框架,在众多项目里都发挥着重要作用。 在日常的项目里,Vue 的组件化开发特性为我带来了极大的便利。组件化能够将页面拆分成多个小的、可复…

深入解析Python爬虫技术:从基础到实战的功能工具开发指南

一、引言:Python 爬虫技术的核心价值 在数据驱动的时代,网络爬虫作为获取公开数据的重要工具,正发挥着越来越关键的作用。Python 凭借其简洁的语法、丰富的生态工具以及强大的扩展性,成为爬虫开发的首选语言。根据 Stack Overflow 2024 年开发者调查,68% 的专业爬虫开发者…

CSS 笔记——Flexbox(弹性盒布局)

目录 1. Flex 容器与 Flex 项目 2. 主轴与交叉轴 3. Flex 容器的属性 display flex-direction justify-content align-items align-content flex-wrap 4. Flex 项目的属性 flex-grow flex-shrink flex-basis flex align-self 5. Flexbox 的优点 6. Flexbox 的…

Java学习手册:Java反射与注解

Java反射(Reflection)和注解(Annotation)是Java语言中两个强大的特性,它们在框架开发和复杂应用中扮演着重要角色。反射允许程序在运行时检查和操作类、对象、接口、字段和方法,而注解则提供了一种元数据形…

JavaWeb遇到的问题汇总

问题一:(键值对最后一项没有逗号) 在JSON字符串转自定义对象和自定义对象转JSON字符串时: 如图所示:若忘记删除键值对的最后一项没有逗号时,则下一句转换不会生效,应该删除最后一项的逗号。 解…

模板引擎语法-变量

模板引擎语法-变量 文章目录 模板引擎语法-变量(一)在Django框架模板中使用变量的代码实例(二)在Django框架模板中使用变量对象属性的代码实例(三)在Django框架模板中使用变量显示列表 (一&…

AUTO-RAG: AUTONOMOUS RETRIEVAL-AUGMENTED GENERATION FOR LARGE LANGUAGE MODELS

Auto-RAG:用于大型语言模型的自主检索增强生成 单位:中科院计算所 代码: https://github.com/ictnlp/Auto-RAG 拟解决问题:通过手动构建规则或者few-shot prompting产生的额外推理开销。 贡献:提出一种以LLM决策为中…

Python 基础语法汇总

Python 语法 │ ├── 基本结构 │ ├── 语句(Statements) │ │ ├── 表达式语句(如赋值、算术运算) │ │ ├── 控制流语句(if, for, while) │ │ ├── 定义语句(def…

一文详解ffmpeg环境搭建:Ubuntu系统ffmpeg配置nvidia硬件加速

在Ubuntu系统下安装FFmpeg有多种方式,其中最常用的是通过apt-get命令和源码编译安装。本文将分别介绍这两种方式,并提供安装过程。 一、apt-get安装 使用apt-get命令安装FFmpeg是最简单快捷的方式,只需要在终端中输入以下命令即可: # 更新软件包列表 sudo apt-get updat…

Android 14 、15动态申请读写权限实现 (Java)

在 Android 14、15 中&#xff0c;Google 进一步优化了存储权限系统&#xff0c;特别是写权限的管理。以下是完整的 Java 实现方案&#xff1a; 1. AndroidManifest.xml 声明权限 <!-- Android 14 存储权限 --> <uses-permission android:name"android.permiss…

小刚说C语言刷题——第23讲 字符数组

前面&#xff0c;我们学习了一维数组和二维数组的概念。今天我们学习一种特殊的数组&#xff0c;字符数组。 1.字符数组的概念 字符数组就是指元素类型为字符的数组。字符数组是用来存放字符序列或者字符串的。 2.字符数组的定义及语法 char ch[5]; 3.字符数组的初始化及赋…

用AI生成系统架构图

DeepSeek+Drawio+SVG绘制架构图-找到一种真正可行实用的方法和思路 1、使用DeepSeek生成SVG文件,导入drawio工具的方法 🔥 问题根源分析 错误现象: • 导入时报错包含 data:image/SVG;base64 和 %20 等 URL 编码字符 • 代码被错误转换为 Base64 格式(适用于网页嵌入,但…

免费干净!付费软件的平替款!

今天给大家分享一款超棒的电脑录屏软件&#xff0c;简直不要太好用&#xff01;它的界面特别干净&#xff0c;没有一点儿广告&#xff0c;看起来特别清爽。 电脑录屏 无广告的录屏软件 这个软件超方便&#xff0c;根本不用安装&#xff0c;打开就能直接用。 它功能也很强大&am…

【XCP实战】AUTOSAR架构下XCP从0到1开发配置实践

目录 前言 正文 1.CAN功能开发 1.1 DBC的制作及导入 1.2 CanTrcv模块配置 1.3 Can Controller模块配置 1.4 CanIf模块配置 2.XCP模块集成配置配置 2.1.XCP模块配置 2.2.XCP模块的Task Mapping 2.3.XCP模块的初始化 3.在链接文件中定义标定段 4.编写标定相关的测试…

Vitis: 使用自定义IP时 Makefile错误 导致编译报错

参考文章: 【小梅哥FPGA】 Vitis开发中自定义IP的Makefile路径问题解决方案 Vitis IDE自定义IP Makefile错误&#xff08;arm-xilinx-eabi-gcc.exe: error: *.c: Invalid argument&#xff09;解决方法 Vitis 使用自定义IP时: Makefile 文件里的语句是需要修改的&#xff0c;…

Python中NumPy的统计运算

在数据分析和科学计算领域&#xff0c;Python凭借其丰富的库生态系统成为首选工具之一&#xff0c;而NumPy作为Python数值计算的核心库&#xff0c;凭借其高效的数组操作和强大的统计运算功能&#xff0c;广泛应用于机器学习、信号处理、统计分析等场景。本文将系统介绍NumPy在…

C语言程序环境和预处理详解

本章重点&#xff1a; 程序的翻译环境 程序的执行环境 详解&#xff1a;C语言程序的编译链接 预定义符号介绍 预处理指令 #define 宏和函数的对比 预处理操作符#和##的介绍 命令定义 预处理指令 #include 预处理指令 #undef 条件编译 程序的翻译环境和执行环…

智能工厂调度系统设计方案研究报告

一、系统架构设计 1.1 物理部署架构 设备层&#xff1a;部署大量搭载多传感器阵列的 AGV 智能循迹车&#xff0c;这些传感器包括激光雷达、视觉相机、超声波传感器等&#xff0c;用于感知周围环境信息&#xff0c;实现自主导航与避障功能&#xff1b;在每个工序节点处设置 RF…