【设计模式】结构型-桥接模式

当抽象与实现,各自独立, 桥接模式,如彩虹桥,连接两岸。

文章目录

  • 一、类爆炸与代码重复
  • 二、桥接模式
  • 三、桥接模式的核心组成
  • 四、运用桥接模式
  • 五、桥接模式的应用场景
  • 六、小结
  • 推荐阅读

一、类爆炸与代码重复

场景假设:假设我们正在设计一个模拟城市交通的系统。在这个系统中,我们有各种类型的桥梁,如悬索桥和拱桥,以及各种类型的车辆,如汽车和自行车。我们希望能够模拟各种车辆在各种桥梁上行驶的情况。

// SuspensionBridgeCar 类代表在悬索桥上驾驶汽车的情况
class SuspensionBridgeCar {public void drive() {System.out.println("Driving a car on a suspension bridge");}
}// ArchBridgeCar 类代表在拱桥上驾驶汽车的情况
class ArchBridgeCar {public void drive() {System.out.println("Driving a car on an arch bridge");}
}// SuspensionBridgeBicycle 类代表在悬索桥上骑自行车的情况
class SuspensionBridgeBicycle {public void drive() {System.out.println("Riding a bicycle on a suspension bridge");}
}// ArchBridgeBicycle 类代表在拱桥上骑自行车的情况
class ArchBridgeBicycle {public void drive() {System.out.println("Riding a bicycle on an arch bridge");}
}public class Main {public static void main(String[] args) {// 创建一个 SuspensionBridgeCar 对象来模拟在悬索桥上驾驶汽车的情况SuspensionBridgeCar suspensionBridgeCar = new SuspensionBridgeCar();suspensionBridgeCar.drive();  // 输出: "Driving a car on a suspension bridge"// 创建一个 ArchBridgeBicycle 对象来模拟在拱桥上骑自行车的情况ArchBridgeBicycle archBridgeBicycle = new ArchBridgeBicycle();archBridgeBicycle.drive();  // 输出: "Riding a bicycle on an arch bridge"// 如果我们想要添加新的桥梁类型或车辆类型,我们需要创建更多的类// 例如,如果我们想要添加“跳绳桥”和“摩托车”,我们需要创建四个新的类:// JumpRopeBridgeCar, JumpRopeBridgeBicycle, SuspensionBridgeMotorcycle, ArchBridgeMotorcycle}
}

在这个例子中,我们可以看到以下问题:

  1. 类的数量爆炸:我们需要为每种桥梁和车辆的组合创建一个新的类。这使得代码难以管理和维护。
  2. 代码重复:在每个子类中,我们可能需要重复相同的代码,这违反了 DRY(Don`t Repeat Yourself)原则。

二、桥接模式

桥接模式是一种结构型设计模式,它主要解决的是“将抽象部分与实现部分分离,使它们可以独立地变化”。它通过提供一个桥接结构,把抽象和实现解耦,使得二者可以独立地变化和复用。

在桥接模式中,有两个独立变化的维度:抽象化(Abstraction)和实现化(Implementation)。抽象化是主要的业务逻辑,而实现化是抽象化依赖的底层实现。桥接模式的目标是将这两个维度分离,使它们可以独立地变化和复用,而不是创建一个包含所有可能组合的类。

三、桥接模式的核心组成

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

  1. 抽象类(Abstraction): 定义了抽象部分的接口,并包含一个指向实现部分对象的引用。抽象类可以包含一些基本操作,而具体的实现则由实现部分提供。
  2. 扩展抽象类(Refined Abstraction): 是对抽象类的扩展,可以添加更多的功能或细化抽象类定义的接口。扩展抽象类通过调用实现部分对象的方法来实现具体的功能。
  3. 实现接口(Implementor): 定义了实现部分的接口,通常包含一组操作方法。实现接口不关心抽象部分的接口,它只负责实现具体的功能。
  4. 具体实现类(Concrete Implementor): 实现了实现接口的具体功能。具体实现类负责实现实现接口定义的操作方法,并与抽象部分的接口进行对应。

在这里插入图片描述

在这个类图中:

  1. Abstraction 是抽象化角色,它定义了基于 Implementor 接口(实现化角色)的高级操作。
  2. RefinedAbstraction 是扩展抽象化角色,它提供了对 Implementor 的具体实现。
  3. Implementor 是实现化角色,它定义了抽象化角色依赖的底层操作。
  4. ConcreteImplementorAConcreteImplementorB 是具体实现化角色,它们实现了 Implementor 接口。

四、运用桥接模式

场景假设:假设我们正在设计一个模拟城市交通的系统。在这个系统中,我们有各种类型的桥梁,如悬索桥和拱桥,以及各种类型的车辆,如汽车和自行车。我们希望能够模拟各种车辆在各种桥梁上行驶的情况。

  1. 创建实现接口: 在桥接模式中,首先我们需要创建一个实现接口,该接口定义了实现部分的操作方法。

    interface Vehicle {// 这个接口定义了一些方法,这些方法将在具体的实现类中实现。void operate();
    }
    
  2. 创建具体实现类: 接着,我们需要创建具体实现类,实现实现接口中定义的具体功能。

    // `Car`实现了`Vehicle`接口,它提供了`operate`方法的具体实现。
    class Car implements Vehicle {public void operate() {System.out.println("car");}
    }// `Bicycle`也实现了`Vehicle`接口,它的实现方式与`Car`类似。
    class Bicycle implements Vehicle {public void operate() {System.out.println("bicycle");}
    }
    
  3. 创建抽象类: 接下来,我们创建一个抽象类,该类将持有一个实现接口的引用,并提供一些操作方法,这些方法将委托给实现接口来实现具体的功能。

    abstract class Bridge {// 这个类包含一个`Vehicle`类型的成员变量,这个变量是实现部分的接口。protected Vehicle vehicle;// 构造函数接收一个`Vehicle`对象,并将其赋值给成员变量。public Bridge(Vehicle vehicle) {this.vehicle = vehicle;}// 定义抽象方法`drive`,具体的实现将在子类中完成。public abstract void drive();
    }
    
  4. 创建具体实现类: 进一步,我们创建具体实现类,继承自抽象类,并实现抽象类中的抽象方法。

    // `SuspensionBridge`是`Bridge`的子类,它实现了`drive`方法。
    class SuspensionBridge extends Bridge {public SuspensionBridge(Vehicle vehicle) {super(vehicle);}public void drive() {System.out.print("Driving on a suspension bridge in a ");vehicle.operate();}
    }// `ArchBridge`也是`Bridge`的子类,它的实现方式与`SuspensionBridge`类似。
    class ArchBridge extends Bridge {public ArchBridge(Vehicle vehicle) {super(vehicle);}public void drive() {System.out.print("Driving on an arch bridge in a ");vehicle.operate();}
    }
    
  5. 在客户端使用桥接模式: 最后,在客户端代码中使用桥接模式,通过创建具体的对象并调用其方法来操作电视。

    // 在客户端代码中,我们创建了一个`SuspensionBridge`对象和一个`Car`对象,并将它们组合在一起。
    public class Main {public static void main(String[] args) {Bridge bridge = new SuspensionBridge(new Car());bridge.drive();  // Output: "Driving on a suspension bridge in a car"}
    }
    

通过上面的桥接模式,通过将抽象(桥梁)和实现(车辆)分离,使它们可以独立地变化。这样,可以更灵活地添加新的桥梁或车辆,而不需要为每种组合创建一个新的类。

五、桥接模式的应用场景

桥接模式主要适用于以下几种场景:

  1. 独立变化的维度:当一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时,可以使用桥接模式。
  2. 不希望使用多重继承或因多重继承导致的类爆炸:桥接模式可以替代多重继承方案,它减少了子类的个数。
  3. 接口或抽象类不稳定:如果系统的抽象部分和具体部分需要独立地变化,那么桥接模式可以保证系统的持续运行。
  4. 重用性要求较高:桥接模式中的抽象部分和实现部分都可以独立地扩展,这有助于提高系统的重用性。

以下是一些具体的应用场景:

  1. 跨平台图形和文字处理软件:在这种软件中,形状和颜色、字体和字号等因素可以独立变化,使用桥接模式可以将这些因素的实现从抽象中分离出来,使得它们可以独立地变化。

    // 形状的抽象类
    abstract class Shape {protected Color color;protected Shape(Color color) {this.color = color;}abstract void draw();
    }// 圆形
    class Circle extends Shape {protected Circle(Color color) {super(color);}void draw() {System.out.println("Draw a circle with " + color.getColor());}
    }// 矩形
    class Rectangle extends Shape {protected Rectangle(Color color) {super(color);}void draw() {System.out.println("Draw a rectangle with " + color.getColor());}
    }// 颜色的接口
    interface Color {String getColor();
    }// 红色
    class Red implements Color {public String getColor() {return "red";}
    }// 蓝色
    class Blue implements Color {public String getColor() {return "blue";}
    }public static void main(String[] args) {Shape redCircle = new Circle(new Red());redCircle.draw();Shape blueRectangle = new Rectangle(new Blue());blueRectangle.draw();
    }
    
  2. 驱动程序:驱动程序中的操作系统和硬件设备是两个独立变化的维度,使用桥接模式可以将它们分离,使得操作系统可以在不同的硬件设备上运行,同时硬件设备也可以接入不同的操作系统。

    // 操作系统的抽象类
    abstract class OperatingSystem {protected Device device;protected OperatingSystem(Device device) {this.device = device;}abstract void run();
    }// Windows 操作系统
    class Windows extends OperatingSystem {protected Windows(Device device) {super(device);}void run() {System.out.println("Run Windows on " + device.getDevice());}
    }// Linux 操作系统
    class Linux extends OperatingSystem {protected Linux(Device device) {super(device);}void run() {System.out.println("Run Linux on " + device.getDevice());}
    }// 硬件设备的接口
    interface Device {String getDevice();
    }// Dell 设备
    class Dell implements Device {public String getDevice() {return "Dell";}
    }// HP 设备
    class HP implements Device {public String getDevice() {return "HP";}
    }public static void main(String[] args) {OperatingSystem windowsOnDell = new Windows(new Dell());windowsOnDell.run();OperatingSystem linuxOnHP = new Linux(new HP());linuxOnHP.run();
    }
    
  3. 数据库连接:在数据库连接中,数据库类型(如 MySQL、Oracle)和连接方式(如 ODBC、JDBC)是两个独立变化的维度,使用桥接模式可以将它们分离,使得可以灵活地组合不同的数据库类型和连接方式。

    // 数据库的抽象类
    abstract class Database {protected Connection connection;protected Database(Connection connection) {this.connection = connection;}abstract void connect();
    }// MySQL 数据库
    class MySQL extends Database {protected MySQL(Connection connection) {super(connection);}void connect() {System.out.println("Connect to MySQL with " + connection.getConnection());}
    }// Oracle 数据库
    class Oracle extends Database {protected Oracle(Connection connection) {super(connection);}void connect() {System.out.println("Connect to Oracle with " + connection.getConnection());}
    }// 连接方式的接口
    interface Connection {String getConnection();
    }// ODBC 连接
    class ODBC implements Connection {public String getConnection() {return "ODBC";}
    }// JDBC 连接
    class JDBC implements Connection {public String getConnection() {return "JDBC";}
    }public static void main(String[] args) {Database mySqlWithODBC = new MySQL(new ODBC());mySqlWithODBC.connect();Database oracleWithJDBC = new Oracle(new JDBC());oracleWithJDBC.connect();
    }
    

六、小结

桥接模式是一种强大的设计模式,它能够有效地解耦抽象和实现,使系统更加灵活和可扩展。通过将抽象部分和实现部分分离,桥接模式使得我们可以轻松地扩展和变化系统的功能,而不会影响原有的代码结构。在实际应用中,我们可以根据具体的需求来选择是否使用桥接模式,以达到更好的设计和开发效果。

推荐阅读

  1. Spring 三级缓存
  2. 深入了解 MyBatis 插件:定制化你的持久层框架
  3. Zookeeper 注册中心:单机部署
  4. 【JavaScript】探索 JavaScript 中的解构赋值
  5. 深入理解 JavaScript 中的 Promise、async 和 await

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

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

相关文章

单片机嵌入式计算器(带程序EXE)

单片机嵌入式计算器 主要功能:完成PWM占空比计算,T溢出时间(延时); [!NOTE] 两个程序EXE; [!CAUTION] 百度网盘链接:链接:https://pan.baidu.com/s/1VJ0G7W5AEQw8_MiagM7g8A?pwdg8…

代码随想录算法训练营第五十四 | ● 392.判断子序列 ● 115.不同的子序列

392.判断子序列 https://programmercarl.com/0392.%E5%88%A4%E6%96%AD%E5%AD%90%E5%BA%8F%E5%88%97.html class Solution { public:bool isSubsequence(string s, string t) {if(s.size()0 )return true;if(t.size()0)return false;vector<vector<int>> dp(s.size(…

为什么选择海外服务器?

如何选择跨境电商服务器&#xff1a;详细指南 选择合适的服务器是跨境电商企业成功的基础。服务器的性能和稳定性直接影响着网站的访问速度、用户体验和安全性&#xff0c;进而影响着企业的销量和利润。那么&#xff0c;跨境电商企业该如何选择服务器呢&#xff1f; ​​​​​…

Jenkins构建 Maven项目(微服务)并自动发布

前面讲了docker 安装Jenkins和gitlab代码管理工具&#xff0c;接下来我们讲一下Jenkins怎么构建 Maven项目。 1. 首先Jenkins配置下面3中工具类 首先是在本地安装三个jenkins自动配置相关的工具 1.1 JDK 由于我们使用docker来启动jenkins&#xff0c;其自带有jdk&#xff0c;…

oracle 12.1 rac to rac adg(maa)搭建保姆级教程

目录 资源配置 一、主库集群操作 1.主库增加standbylog 2.主库开启force logging及归档 3.主库配置参数 4.生成参数文件并将参数文件、密码文件拷贝至备库 4.1参数文件处理 4.2密码文件处理 二、备库操作 1.备库修改参数文件 1.1创建adump目录并在参数文件修改&#…

02-JAVA面向对象编程

一、面向对象编程 1、面向过程编程思想&#xff08;Process Oritented Programming&#xff09; 将实现一个功能的一组指令组合在一起&#xff0c;成为一个函数。这个函数就能实现这一个功能&#xff0c;是对功能实现的一种抽象。通过这种抽象方式&#xff0c;将代码实现复用。…

代码随想录算法训练营第三十一天| 455.分发饼干,376. 摆动序列 ,53. 最大子序和

455. 分发饼干 - 力扣&#xff08;LeetCode&#xff09; class Solution {public int findContentChildren(int[] g, int[] s) {Arrays.sort(g); //递增Arrays.sort(s); int result 0;//遍历&#xff0c;先满足小的int i0,j0;for(;i<g.length && j<s.length;i){…

视觉大模型(VLLM)学习笔记

视觉多模态大模型&#xff08;VLLM&#xff09; InternVL 1.5 近日&#xff0c;上海人工智能实验室 OpenGVLab 团队、清华大学、商汤科技合作推出了开源多模态大语言模型项目InternVL 1.5&#xff0c;它不仅挑战了商业模型巨头例如 GPT-4V 的霸主地位&#xff0c;还让我们不禁…

golang基础

Go安装及配置环境 下载最新的 zip 文件: go#.#.#.windows-amd64.zip &#xff0c;这里的 #.#.# 是 Go 的最新版本号。 解压缩 go#.#.#.windows-amd64.zip 文件到你选择的位置。比如D:\Go 在系统中设置两个环境变量&#xff1a;GOROOT和GOPATH GOPATH 指向的是你的工作目录。…

树莓派4B 零起点(三) 树莓派 VNC 远程桌面配置(2) 配置X11模式

目录 一、配置 VNC Server为X11 1、关闭已启动的VNC Server (如之前未开启&#xff0c;此步可以忽略) 2、切换 VNC Server的模式为 X11 二、开启 X11 VncServer 三、修改树莓派VNC Server的连接模式 1、切换到 root 账号 2、修改VNC Server X11的配置 3、设置VNC密码…

引人入胜的教育视频

对于一家专注于数字自动化和能源管理的跨国公司&#xff0c;我们制作了引人入胜的教育视频&#xff0c;帮助房主选择适合他们需求的电气产品。我们的团队审查并定稿文本&#xff0c;录制并编辑配音&#xff0c;选择背景音乐&#xff0c;设计图形&#xff0c;并制作了演示如何安…

MYSQL基础_02_MySQL环境搭建

第02章_MySQL环境搭建 1. MySQL的卸载 步骤1&#xff1a;停止MySQL服务 在卸载之前&#xff0c;先停止MySQL8.0的服务。按键盘上的“Ctrl Alt Delete”组合键&#xff0c;打开“任务管理器”对话框&#xff0c;可以在“服务”列表找到“MySQL8.0”的服务&#xff0c;如果现…

Springboot校园美食推荐系统的开发-计算机毕业设计源码44555

摘要 随着人们生活水平的提高&#xff0c;人们对美食的要求也越来越高&#xff0c;对各类美食信息需求越来越大。因此&#xff0c;结合计算机快速发展、普及&#xff0c;在此基础上制作一个页面简单、美观,功能实用的校园美食推荐系统势在必行&#xff0c;满足用户分享美食的需…

前端nvm的安装和使用nodejs多版本管理2024

nvm的安装和使用 1、简介 nvm是一个管理nodejs版本的工具。在实际的开发中&#xff0c;项目的开发依赖需要的nodejs版本运行环境不同&#xff0c;此时我们就需要使用nvm来进行不同nodejs版本的切换。其实就是一个方便的node版本管理工具。 注意&#xff1a;如果有安装过node&a…

nw.js 如何调用activeX控件 (控件是C++编写的dll文件)

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

第1章Hello world 4/5:对比Rust/Java/C++创建和运行Hello world全过程:运行第一个程序

讲动人的故事,写懂人的代码 1.7 对比Rust/Java/C++创建和运行Hello world全过程 有了会听懂人类的讲话,还能做记录的编程助理艾极思,他们三人的讨论内容,都可以变成一份详细的会议纪要啦。 接下来,我们一起看看艾极思是如何记录下赵可菲创建和运行Java程序Hello world,…

本地搭建支持语音和文本的中英文翻译服务-含全部源代码

实现目标 1、支持文本中英文互译&#xff1b; 2、支持中文语音输入&#xff1b; 3、支持英文语言输入&#xff1b; 进阶&#xff08;未实现&#xff09; 4、优化web界面&#xff1b; 5、优化语音输入js实现逻辑&#xff1b; 6、增加语音输入自纠错模型&#xff0c;纠正语音识别…

代码随想录算法训练营第三十二天| 122.买卖股票的最佳时机II,55. 跳跃游戏 ,45.跳跃游戏II

122. 买卖股票的最佳时机 II - 力扣&#xff08;LeetCode&#xff09; class Solution {public int maxProfit(int[] prices) {if(prices.length 0){return 0;}int min prices[0];int result 0;for(int i1;i<prices.length;i){if(prices[i] > min){result (prices[i]…

“双一流名校”苏州大学计算机专业好考吗?苏州大学计算机考研考情分析

苏州大学&#xff08;Soochow University&#xff09;&#xff0c;简称“苏大”&#xff0c;坐落于历史文化名城苏州&#xff0c;国家“211工程”重点建设高校&#xff0c;国家国防科技工业局和江苏省人民政府共建高校&#xff0c;国家“双一流”世界一流学科建设高校&#xff…