当前位置: 首页 > news >正文

【设计模式】Java 设计模式之桥接模式(Bridge)

桥接模式(Bridge Pattern)是结构型设计模式的一种,它主要解决的是抽象部分与实现部分的解耦问题,使得两者可以独立变化。这种类型的设计模式属于结构型模式,因为该模式涉及如何组合接口和它们的实现。将抽象部分与实现部分分离,使它们都可以独立地变化。

一、桥接模式概述

桥接模式的主要思想是将抽象与实现进行解耦,使得二者可以独立进行变化。在桥接模式中,抽象部分和实现部分被分离出来,抽象部分定义了一个抽象接口,这个接口是抽象部分与实现部分进行交互的桥梁。实现部分则是对这个接口的具体实现。

二、桥接模式结构

桥接模式主要包含四个角色:

  1. 抽象接口(Abstraction):定义了一个抽象接口,用于与实现部分进行交互。
  2. 扩展抽象类(RefinedAbstraction):实现抽象接口,并调用实现部分。
  3. 实现接口(Implementor):定义了实现部分的接口。
  4. 具体实现类(ConcreteImplementor):实现实现接口,完成具体的操作。

三、桥接模式的实现方式

在桥接模式中,抽象接口和实现接口是解耦的,因此抽象接口和实现接口可以独立变化,而不会影响到对方。这种解耦使得系统更加灵活,能够适应更多的变化。

下面是一个简单的桥接模式的实现代码示例:

// 实现接口
public interface Implementor {void operationImpl();
}// 具体实现类
public class ConcreteImplementorA implements Implementor {@Overridepublic void operationImpl() {System.out.println("ConcreteImplementorA operation implementation.");}
}public class ConcreteImplementorB implements Implementor {@Overridepublic void operationImpl() {System.out.println("ConcreteImplementorB operation implementation.");}
}// 抽象接口
public abstract class Abstraction {protected Implementor implementor;public Abstraction(Implementor implementor) {this.implementor = implementor;}public abstract void operation();
}// 扩展抽象类
public class RefinedAbstractionA extends Abstraction {public RefinedAbstractionA(Implementor implementor) {super(implementor);}@Overridepublic void operation() {System.out.println("RefinedAbstractionA operation.");implementor.operationImpl();}
}public class RefinedAbstractionB extends Abstraction {public RefinedAbstractionB(Implementor implementor) {super(implementor);}@Overridepublic void operation() {System.out.println("RefinedAbstractionB operation.");implementor.operationImpl();}
}

四、桥接模式的优缺点

桥接模式的优点主要有:

  1. 分离抽象接口及其实现部分。桥接模式使得抽象接口与实现部分可以独立变化,不会影响到对方,从而提高了系统的可扩展性。
  2. 提高系统的可维护性。由于抽象与实现的分离,使得系统可以更加容易地进行维护和升级。
  3. 实现细节对客户透明。客户端只与抽象接口进行交互,无需关心具体的实现细节。

桥接模式的缺点主要有:

  1. 增加了系统的理解与设计难度。由于引入了抽象层与实现层的分离,使得系统的结构变得相对复杂,增加了理解和设计的难度。
  2. 需要更多的类。桥接模式引入了抽象接口、实现接口以及多个具体实现类,从而增加了系统的类数量。

五、桥接模式的应用场景

桥接模式通常适用于以下场景:

  1. 当一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时。
  2. 当不希望使用继承来造成类爆炸时。
  3. 当一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性时。

六、桥接模式的应用案例

以图形绘制系统为例,我们可以将图形的形状(如圆形、矩形等)作为抽象接口,将不同的绘制方式(如使用OpenGL、DirectX等)作为实现接口。这样,我们可以根据需求动态地组合不同的形状和绘制方式,实现灵活的图形绘制。例如,我们可以创建一个圆形对象,并指定使用OpenGL进行绘制;或者创建一个矩形对象,并指定使用DirectX进行绘制。这种组合方式使得系统的扩展性和灵活性得到了极大的提升。
六、桥接模式的应用案例(续)

以图形绘制系统为例,我们可以进一步细化桥接模式的应用。

首先,我们定义一个图形绘制的抽象接口,这个接口描述了一个图形对象应该具有的基本行为:

// 图形绘制抽象接口
public interface Shape {void draw();void resize();
}

然后,我们定义具体的图形实现类,这些类实现了Shape接口,并且各自代表一种图形类型:

// 圆形实现类
public class Circle implements Shape {@Overridepublic void draw() {System.out.println("Drawing Circle...");}@Overridepublic void resize() {System.out.println("Resizing Circle...");}
}// 矩形实现类
public class Rectangle implements Shape {@Overridepublic void draw() {System.out.println("Drawing Rectangle...");}@Overridepublic void resize() {System.out.println("Resizing Rectangle...");}
}

接下来,我们定义绘制API的接口,这个接口描述了如何绘制一个图形:

// 绘制API接口
public interface DrawingAPI {void drawCircle(int x, int y, int radius);void drawRectangle(int x1, int y1, int x2, int y2);
}

然后,我们实现具体的绘制API,这些类实现了DrawingAPI接口,并且各自使用不同的图形库或技术进行绘制:

// 使用OpenGL的绘制实现类
public class OpenGLDrawingAPI implements DrawingAPI {@Overridepublic void drawCircle(int x, int y, int radius) {System.out.println("Drawing Circle using OpenGL...");}@Overridepublic void drawRectangle(int x1, int y1, int x2, int y2) {System.out.println("Drawing Rectangle using OpenGL...");}
}// 使用DirectX的绘制实现类
public class DirectXDrawingAPI implements DrawingAPI {@Overridepublic void drawCircle(int x, int y, int radius) {System.out.println("Drawing Circle using DirectX...");}@Overridepublic void drawRectangle(int x1, int y1, int x2, int y2) {System.out.println("Drawing Rectangle using DirectX...");}
}

现在,我们需要一个桥接类来连接ShapeDrawingAPI。这个桥接类持有对DrawingAPI的引用,并且根据具体的Shape类型调用相应的绘制方法:

// 桥接类
public abstract class Bridge {protected DrawingAPI drawingAPI;public Bridge(DrawingAPI drawingAPI) {this.drawingAPI = drawingAPI;}public abstract void draw();public abstract void resize();
}// 圆形的桥接实现
public class CircleBridge extends Bridge {private Circle circle;public CircleBridge(DrawingAPI drawingAPI) {super(drawingAPI);this.circle = new Circle();}@Overridepublic void draw() {// 调用具体的绘制API来绘制圆形drawingAPI.drawCircle(0, 0, 50);circle.draw();}@Overridepublic void resize() {circle.resize();}
}// 矩形的桥接实现
public class RectangleBridge extends Bridge {private Rectangle rectangle;public RectangleBridge(DrawingAPI drawingAPI) {super(drawingAPI);this.rectangle = new Rectangle();}@Overridepublic void draw() {// 调用具体的绘制API来绘制矩形drawingAPI.drawRectangle(0, 0, 100, 100);rectangle.draw();}@Overridepublic void resize() {rectangle.resize();}
}

现在,客户端代码可以动态地组合不同的图形和绘制API:

public class Client {public static void main(String[] args) {// 创建OpenGL绘制API实例DrawingAPI openGL = new OpenGLDrawingAPI();// 使用OpenGL绘制圆形的桥接对象Bridge circleBridge = new CircleBridge(openGL);circleBridge.draw(); // 绘制圆形,使用OpenGL技术// 创建DirectX绘制API实例DrawingAPI directX = new DirectXDrawingAPI();// 使用DirectX绘制矩形的桥接对象Bridge rectangleBridge = new RectangleBridge(directX);rectangleBridge.draw(); // 绘制矩形,使用DirectX技术// 可以根据需要替换不同的绘制API,而不影响图形对象// 例如,将圆形的绘制API更改为DirectXcircleBridge = new CircleBridge(directX);circleBridge.draw(); // 现在圆形使用DirectX技术进行绘制}
}

在这个例子中,桥接模式允许我们独立地改变图形的实现和绘制API的实现。客户端代码可以动态地组合不同的图形和绘制技术,而不需要修改现有的代码。这提供了极大的灵活性和可扩展性。

桥接模式的关键在于将抽象部分与实现部分解耦,使得它们可以独立地变化。在这个图形绘制系统中,Shape接口及其实现类代表了抽象部分,而DrawingAPI接口及其实现类代表了实现部分。Bridge类及其子类充当了两者之间的桥梁,允许它们以松耦合的方式组合在一起。

通过这种方式,桥接模式可以有效地管理具有多维度变化的系统,提高了代码的可维护性和复用性。同时,它也使得系统更加灵活,能够轻松地适应新的需求变化。
七、桥接模式的优缺点

优点:

  1. 分离抽象与实现:桥接模式将抽象部分与实现部分分离开来,使得它们可以独立变化。这种分离降低了类之间的耦合度,提高了系统的可维护性和可扩展性。

  2. 提高系统可扩展性:桥接模式使得抽象部分和实现部分都可以独立扩展。例如,可以添加新的图形类型而不需要修改现有的绘制API,反之亦然。

  3. 设计灵活:桥接模式使得客户端代码可以动态地组合不同的抽象部分和实现部分,提供了极大的设计灵活性。

  4. 符合开闭原则:桥接模式符合开闭原则,即对扩展开放,对修改封闭。当需要添加新的功能时,可以通过扩展新的类来实现,而不需要修改现有的代码。

缺点:

  1. 增加系统复杂性:桥接模式相比于其他设计模式,其结构相对复杂,引入了更多的类和接口。这可能会增加系统的复杂性,使得初学者难以理解和使用。

  2. 可能导致性能损失:由于桥接模式引入了额外的间接层,可能会导致一些性能上的损失。然而,这种损失通常是可以接受的,因为桥接模式带来的好处远超过了这一点点性能损失。

  3. 需要更多的设计工作:使用桥接模式需要更多的设计工作,需要仔细考虑哪些部分应该作为抽象部分,哪些部分应该作为实现部分,以及如何设计它们之间的桥接关系。

八、桥接模式的应用场景

桥接模式适用于以下场景:

  1. 需要多维度变化的系统:当系统需要在多个维度上进行变化时,可以使用桥接模式来分离这些变化,使得系统更加灵活和可维护。

  2. 抽象和实现需要独立变化的场景:当抽象部分和实现部分需要独立变化时,桥接模式可以将它们解耦,使得它们可以独立地扩展和修改。

  3. 不希望使用继承的场景:当系统中存在大量的继承关系,导致类层次结构过于复杂时,可以考虑使用桥接模式来替代继承,减少类之间的耦合度。

  4. 需要跨平台或跨技术的场景:当系统需要支持多种平台或技术时,可以使用桥接模式来抽象出共同的接口和行为,使得不同的平台或技术可以通过桥接类来实现。

九、桥接模式的实际应用

桥接模式在实际开发中有着广泛的应用,特别是在需要处理多维度变化或者需要跨平台、跨技术实现的场景中。以下是一些桥接模式在实际项目中的应用示例:

  1. 图形用户界面(GUI)框架
    GUI框架中常常包含多种组件(如按钮、文本框等)和多种渲染方式(如Windows风格、Mac风格等)。使用桥接模式,可以将组件的抽象接口与具体的渲染实现分离,使得开发者可以灵活地组合不同的组件和渲染方式。

  2. 跨平台应用开发
    在开发跨平台应用时,不同平台可能有不同的API和特性。桥接模式可以将平台相关的实现与平台无关的抽象分离,使得开发者可以编写一次代码,然后在多个平台上运行。

  3. 消息处理系统
    在一个复杂的消息处理系统中,可能有多种消息类型(如订单消息、库存消息等)和多种处理方式(如实时处理、批量处理等)。桥接模式可以将消息类型的抽象与处理方式的具体实现分离,使得系统更加灵活和可扩展。

  4. 插件式架构
    在插件式架构中,主程序提供了统一的接口,而插件则实现了这些接口的具体功能。桥接模式可以用于将主程序的抽象接口与插件的具体实现分离,使得主程序可以动态地加载和卸载插件,实现功能的扩展和定制。

十、桥接模式与其他模式的比较

桥接模式与其他设计模式在某些方面有相似之处,但也有明显的区别。以下是一些常见的模式比较:

  1. 与适配器模式比较

    • 适配器模式主要用于将一个类的接口转换成客户端所期望的另一种接口,使得原本不兼容的类可以一起工作。它主要关注接口的转换。
    • 桥接模式则更侧重于将抽象部分与实现部分分离,使得它们可以独立变化。它主要关注结构的分离和组合。
  2. 与策略模式比较

    • 策略模式定义了一系列算法,并将每一个算法封装起来,使它们可以互相替换。策略模式使得算法可以独立于使用它的客户端变化。
    • 桥接模式与策略模式在某种程度上都是关于变化的分离,但桥接模式更侧重于抽象与实现的分离,而策略模式更侧重于算法的替换。
  3. 与组合模式比较

    • 组合模式允许你将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得客户端对单个对象和复合对象的使用具有一致性。
    • 桥接模式与组合模式在处理对象的组合方面有所不同。桥接模式关注于抽象与实现的分离,而组合模式则关注于对象的层次结构和组合关系。

十一、总结

桥接模式是一种强大的设计模式,它通过分离抽象与实现,使得系统能够灵活地应对多维度变化。在实际开发中,桥接模式可以应用于各种需要独立扩展抽象和实现的场景,如GUI框架、跨平台应用开发、消息处理系统和插件式架构等。然而,在使用桥接模式时,也需要权衡其带来的复杂性和性能损失。在设计系统时,应根据具体需求和场景来选择合适的设计模式,以实现更好的可维护性、可扩展性和灵活性。

相关文章:

【设计模式】Java 设计模式之桥接模式(Bridge)

桥接模式(Bridge Pattern)是结构型设计模式的一种,它主要解决的是抽象部分与实现部分的解耦问题,使得两者可以独立变化。这种类型的设计模式属于结构型模式,因为该模式涉及如何组合接口和它们的实现。将抽象部分与实现…...

记录dockers中Ubuntu安装python3.11

参考: docker-ubuntu 安装python3.8,pip3_dockerfile ubuntu22 python3.8-CSDN博客...

【算法专题--双指针算法】leetcode--283. 移动零、leetcode--1089. 复写零

🍁你好,我是 RO-BERRY 📗 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 🎄感谢你的陪伴与支持 ,故事既有了开头,就要画上一个完美的句号,让我们一起加油 目录 前言1. 移动零&#xff0…...

【JavaEE -- 多线程3 - 多线程案例】

多线程案例 1.单例模式1.1 饿汉模式的实现方法1.2 懒汉模式的实现方法 2. 阻塞队列2.1 引入生产消费者模型的意义:2.2 阻塞队列put方法和take方法2.3 实现阻塞队列--重点 3.定时器3.1 定时器的使用3.2 实现定时器 4 线程池4.1 线程池的使用4.2 实现一个简单的线程池…...

k8s的pod服务升级,通过部署helm升级

要通过Helm升级Kubernetes(k8s)中的Pod服务,你可以按照以下步骤进行操作: 安装Helm: 如果你还没有安装Helm,可以通过官方文档提供的方式进行安装。添加Helm仓库: 确保你已经添加了包含你要升级…...

复现文件上传漏洞

一、搭建upload-labs环境 将下载好的upload-labs的压缩包,将此压缩包解压到WWW中,并将名称修改为upload,同时也要在upload文件中建立一个upload的文件。 然后在浏览器网址栏输入:127.0.0.1/upload进入靶场。 第一关 选择上传文件…...

Java 内存异常

内存溢出 内存溢出指的是在程序执行过程中,申请的内存超过了系统实际可用的内存资源。 内存溢出的常见情况: 创建大量对象并持有引用:在程序中创建大量对象并持有对这些对象的引用,而没有及时释放这些引用,导致堆内存…...

Windows11去掉 右键菜单的 AMD Software:Adrenalin Edition 选项

Windows11去掉 右键菜单的 AMD Software:Adrenalin Edition 选项 运行regedit打开注册表编辑器 先定位到 计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Classes\PackagedCom\Package 计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Classes\PackagedCom\Package找到 AdvancedMicroDevicesInc-2.…...

uniapp实现我的订单页面无感 - 删除数据

在进入我们的订单页面时进行获取列表,上拉加载,下拉刷新等请求,我们在删除数据时,请求删除接口后,不要重新去请求数据,不要重新去请求数据,不要重新去请求数据 重新请求会刷新页面中的数据 方…...

MySQL—redo log、undo log以及MVCC

MySQL—redo log、undo log以及MVCC 首先回忆一下MySQL事务的四大特性:ACID,即原子性、一致性、隔离性和持久性。其中原子性、一致性、持久性实际上是由InnoDB中的两份日志保证的,一份是redo log日志,一份是undo log日志&#xff…...

13 list的实现

注意 实现仿cplus官网的list类&#xff0c;对部分主要功能实现 实现 文件 #pragma once #include <assert.h>namespace mylist {template <typename T>struct __list_node{__list_node(const T& x T()): _prev(nullptr), _next(nullptr), _data(x){}__lis…...

如何用client-go获取k8s因硬盘容量、cpu、内存、gpu资源不够引起的错误信息?

在Kubernetes中&#xff0c;你可以使用client-go库来获取Pod的状态和事件&#xff0c;这些信息可能包含了由于资源不足引起的错误信息。 以下是一个基本的示例&#xff0c;展示如何使用client-go来获取Pod的状态和事件&#xff1a; package mainimport ("flag"&quo…...

IDEA编译安卓源码TVBox(2)

一、项目结构&#xff1a;主要app和player app结构 二、增加遥控器按键选台 修改LivePlayActivity.java 1、声明变量 public String channelId "";public Timer timer new Timer();public Toast mToast;2、定义方法 private void mToastShow(String s){mToast …...

【C#】.net core 6.0 使用第三方日志插件Log4net,配置文件详细说明

欢迎来到《小5讲堂》 大家好&#xff0c;我是全栈小5。 这是《C#》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对知识点的理解和掌握。…...

第十四届蓝桥杯省赛真题 Java 研究生 组【原卷】

文章目录 发现宝藏【考生须知】试题 A: 特殊日期试题 B: 与或异或试题 C: 棋盘试题 D: 子矩阵试题 E : \mathrm{E}: E: 互质数的个数试题 F: 小蓝的旅行计划试题 G: 奇怪的数试题 H: 太阳试题 I: 高塔试题 J \mathrm{J} J : 反异或 01 串 发现宝藏 前些天发现了一个巨牛的人…...

adb shell input text 输入中文

由于adb 不支持中文输入&#xff08;不支持 Unicode&#xff09;&#xff0c;需要使用虚拟键盘绕一圈。 可以直接参考和使用&#xff1a; https://github.com/senzhk/ADBKeyBoard # 通用方式 adb shell am broadcast -a ADB_INPUT_TEXT --es msg 赞 # mac/linux 支持 base64…...

Rudolf and the Ball Game

传送门 题意 思路 暴力枚举每一个妆台的转换条件 code #include<iostream> #include<cstdio> #include<stack> #include<vector> #include<algorithm> #include<cmath> #include<queue> #include<cstring> #include<ma…...

计算机毕业设计-基于大数据技术下的高校舆情监测与分析

收藏和点赞&#xff0c;您的关注是我创作的动力 文章目录 概要 一、研究背景与意义1.1背景与意义1.2 研究内容 二、舆情监测与分析的关键技术2.1 robot协议对本设计的影响2.2 爬虫2.2.1 工作原理2.2.2 工作流程2.2.3 抓取策略2.3 scrapy架构2.3.1 scrapy&#xff1a;开源爬虫架…...

WPF使用LiveCharts画图时,横坐标转换成时间

一、背景 使用LiveCharts画图时&#xff0c;横坐标通常为数值类型&#xff0c;要转换成时间等自定义类型&#xff0c;需要用到Formatter进行类型转换。 示例使用MVVM模式编写 二、View代码 关键是设置LabelFormatter属性 <lvc:CartesianChart Series"{Binding Series…...

Qt客户端开发的技术难点

在Qt客户端开发中&#xff0c;可能会遇到一些技术难点&#xff0c;这些难点可能与UI设计、性能优化、跨平台兼容性等方面有关。以下是一些可能的技术难点&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法

树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作&#xff0c;无需更改相机配置。但是&#xff0c;一…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来

一、破局&#xff1a;PCB行业的时代之问 在数字经济蓬勃发展的浪潮中&#xff0c;PCB&#xff08;印制电路板&#xff09;作为 “电子产品之母”&#xff0c;其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透&#xff0c;PCB行业面临着前所未有的挑战与机遇。产品迭代…...

Unity3D中Gfx.WaitForPresent优化方案

前言 在Unity中&#xff0c;Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染&#xff08;即CPU被阻塞&#xff09;&#xff0c;这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案&#xff1a; 对惹&#xff0c;这里有一个游戏开发交流小组&…...

Linux简单的操作

ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...

生成 Git SSH 证书

&#x1f511; 1. ​​生成 SSH 密钥对​​ 在终端&#xff08;Windows 使用 Git Bash&#xff0c;Mac/Linux 使用 Terminal&#xff09;执行命令&#xff1a; ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" ​​参数说明​​&#xff1a; -t rsa&#x…...

Python爬虫(二):爬虫完整流程

爬虫完整流程详解&#xff08;7大核心步骤实战技巧&#xff09; 一、爬虫完整工作流程 以下是爬虫开发的完整流程&#xff0c;我将结合具体技术点和实战经验展开说明&#xff1a; 1. 目标分析与前期准备 网站技术分析&#xff1a; 使用浏览器开发者工具&#xff08;F12&…...

【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)

升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点&#xff0c;但无自动故障转移能力&#xff0c;Master宕机后需人工切换&#xff0c;期间消息可能无法读取。Slave仅存储数据&#xff0c;无法主动升级为Master响应请求&#xff…...

大学生职业发展与就业创业指导教学评价

这里是引用 作为软工2203/2204班的学生&#xff0c;我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要&#xff0c;而您认真负责的教学态度&#xff0c;让课程的每一部分都充满了实用价值。 尤其让我…...

tree 树组件大数据卡顿问题优化

问题背景 项目中有用到树组件用来做文件目录&#xff0c;但是由于这个树组件的节点越来越多&#xff0c;导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多&#xff0c;导致的浏览器卡顿&#xff0c;这里很明显就需要用到虚拟列表的技术&…...

大数据学习(132)-HIve数据分析

​​​​&#x1f34b;&#x1f34b;大数据学习&#x1f34b;&#x1f34b; &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 用力所能及&#xff0c;改变世界。 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4…...