【设计模式——学习笔记】23种设计模式——桥接模式Bridge(原理讲解+应用场景介绍+案例介绍+Java代码实现)

问题引入

现在对不同手机类型的不同品牌实现操作编程(比如:开机、关机、上网,打电话等),如图

在这里插入图片描述

【对应类图】

在这里插入图片描述

【分析】

  • 扩展性问题(类爆炸),如果我们再增加手机的样式(旋转式),就需要增加各个品牌手机的类,同样如果我们增加一个手机品牌,也要在各个手机样式类下增加。
  • 违反了单一职责原则,当我们增加手机样式时,要同时增加所有品牌的手机,这样增加了代码维护成本
  • 解决方案:使用桥接模式

介绍

基础介绍

  • 桥接模式(Bridge模式)是指: 将实现与抽象放在两个不同的类层次中,使两个层次可以独立改变。
  • Bridge 的意思是“桥梁”。就像在现实世界中,桥梁的功能是将河流的两侧连接起来一样Bridge 模式的作用也是将两样东西连接起来,它们分别是类的功能层次结构和类的实现层次结构。
  • 是一种结构型设计模式
  • Bridge模式基于类的最小设计原则(实现功能的同时,让类尽可能少),通过使用封装、聚合及继承等行为让不同的类承担不同的职责。它的主要特点是把抽象(Abstraction)与行为实现(lmplementation)分离开来,从而可以保持各部分的独立性以及应对他们的功能扩展

类的功能层次结构

假设现在有一个类Something。当我们想在Something中增加新功能时(想增加一个具体方法时),会编写一个Something类的子类(派生类),如SomethingGood类。这样就构成了一个小小的类层次结构。

在这里插入图片描述

父类本身具备一些基本功能,子类在继承父类的功能之外,还可以添加新的功能。这种层次结构被称为“类的功能层次结构”。如果需要在SomethingGood的基础上继续增加新的功能,再写SomethingGood的子类即可。

在这里插入图片描述

注意:类的层次结构关系不应当过深

类的实现层次结构

抽象类声明了一些抽象方法,定义了接口(API),然后子类负责去实现这些抽象方法。父类的任务是通过声明抽象方法的方式定义接口(API),而子类的任务是实现抽象方法。正是由于父类和子类的这种任务分担,我们才可以编写出具有高可替换性的类。

当子类Concreteclass实现了父类Abstractclass类的抽象方法时,它们之间就构成了一个小小的层次结构。

在这里插入图片描述

这种类的层次结构(类的实现层次结构)并非用于增加功能,并不方便我们增加新的方法。它的真正作用是帮助我们实现下面这样的任务分担

  • 类通过声明抽象方法来定义接口(API)
  • 子类通过实现具体方法来实现接口(API)

一个抽象类可以有多种子实现类

在这里插入图片描述

层次结构分离

当类的层次结构只有一层时,功能层次结构与实现层次结构是混杂在一个层次结构中的。这样很容易使类的层次结构变得复杂,也难以透彻地理解类的层次结构。因为自己难以确定究竟应该在类的哪一个层次结构中去增加子类。

因此,我们需要将“类的功能层次结构”与“类的实现层次结构”分离为两个独立的类层次结构。当然,如果只是简单地将它们分开,两者之间必然会缺少联系。所以我们还需要在它们之间搭建一座桥梁,这就是桥接模式。

类图

在这里插入图片描述

抽象类和接口是聚合关系,也是调用和被调用关系

应用场景

对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用,如以下场景:

  • JDBC驱动程序
  • 银行转账系统
    • 转账分类: 网上转账,柜台转账,AMT转账
    • 转账用户类型: 普通用户,银卡用户,金卡用户
  • 消息管理
    • 消息类型: 即时消息,延时消息
    • 消息分类: 手机短信,邮件消息,QQ消息…

案例

案例一

类图

在这里插入图片描述

代码实现

【接口】

package com.atguigu.bridge;/*** 接口*/
public interface Brand {void open();void close();void call();
}

【接口实现类:小米手机】

package com.atguigu.bridge;public class XiaoMi implements Brand {@Overridepublic void open() {System.out.println(" 小米手机开机 ");}@Overridepublic void close() {System.out.println(" 小米手机关机 ");}@Overridepublic void call() {System.out.println(" 小米手机打电话 ");}}

【接口实现类:Vivo手机】

package com.atguigu.bridge;public class Vivo implements Brand {@Overridepublic void open() {System.out.println(" Vivo手机开机 ");}@Overridepublic void close() {System.out.println(" Vivo手机关机 ");}@Overridepublic void call() {System.out.println(" Vivo手机打电话 ");}}

【抽象类】

package com.atguigu.bridge;public abstract class Phone {/*** 聚合品牌*/private Brand brand;/*** 构造器* @param brand*/public Phone(Brand brand) {super();this.brand = brand;}protected void open() {this.brand.open();}protected void close() {this.brand.close();}protected void call() {this.brand.call();}}

【抽象类子类:折叠手机】

package com.atguigu.bridge;/*** 折叠式手机类,继承 抽象类 Phone*/
public class FoldPhone extends Phone {//构造器public FoldPhone(Brand brand) {super(brand);}public void open() {System.out.println(" 折叠样式手机 ");// 实际上调用的是具体品牌(如Xiaomi)的开机方法,抽象类Phone充当桥接作用super.open();}public void close() {System.out.println(" 折叠样式手机 ");super.close();}public void call() {System.out.println(" 折叠样式手机 ");super.call();}
}

【抽象类子类:直立手机】

package com.atguigu.bridge;public class UpRightPhone extends Phone {//构造器public UpRightPhone(Brand brand) {super(brand);}public void open() {System.out.println(" 直立样式手机 ");super.open();}public void close() {System.out.println(" 直立样式手机 ");super.close();}public void call() {System.out.println(" 直立样式手机 ");super.call();}
}

【客户端】

package com.atguigu.bridge;public class Client {public static void main(String[] args) {//获取折叠式手机 (样式 + 品牌 才是具体的手机)Phone phone1 = new FoldPhone(new XiaoMi());phone1.open();phone1.call();phone1.close();System.out.println("==============");Phone phone2 = new FoldPhone(new Vivo());phone2.open();phone2.call();phone2.close();System.out.println("==============");UpRightPhone phone3 = new UpRightPhone(new XiaoMi());phone3.open();phone3.call();phone3.close();System.out.println("==============");UpRightPhone phone4 = new UpRightPhone(new Vivo());phone4.open();phone4.call();phone4.close();}}

【运行】

折叠样式手机 小米手机开机 折叠样式手机 小米手机打电话 折叠样式手机 小米手机关机 
==============折叠样式手机 Vivo手机开机 折叠样式手机 Vivo手机打电话 折叠样式手机 Vivo手机关机 
==============直立样式手机 小米手机开机 直立样式手机 小米手机打电话 直立样式手机 小米手机关机 
==============直立样式手机 Vivo手机开机 直立样式手机 Vivo手机打电话 直立样式手机 Vivo手机关机 Process finished with exit code 0

分析

  • 无论增加一个样式或者增加一个新的手机品牌,都只需要增加一个类

案例二

类图

在这里插入图片描述

Display使用DispalyImpl的方法来完成功能,StringDisplayImpl负责DispalyImpl方法的具体实现,CountDisplay用来完成更多的功能

代码实现

【类的功能层次结构:Display】

package com.atguigu.bridge.Sample;/*** 类的功能层次最上层*/
public class Display {/*** 实现了Display类的具体功能的实例 (桥梁)*/private DisplayImpl impl;public Display(DisplayImpl impl) {this.impl = impl;}/*** 显示前的处理*/public void open() {impl.rawOpen();}/*** 显示处理*/public void print() {impl.rawPrint();}/*** 显示后的处理*/public void close() {impl.rawClose();}/*** 调用上面的三个方法来进行显示*/public final void display() {open();print();                    close();}
}

private DisplayImpl impl;使用了“委托”关系,而不是使用“继承”关系。继承是强关联关系,委托是弱关联关系。使用委托更方便扩展。

【类的功能层次结构:CountDisplay】

package com.atguigu.bridge.Sample;/*** 在Display类的基础上增加新功能*/
public class CountDisplay extends Display {public CountDisplay(DisplayImpl impl) {super(impl);}/*** 循环显示times次** @param times*/public void multiDisplay(int times) {open();for (int i = 0; i < times; i++) {print();}close();}
}

【类的实现层次结构:上层】

package com.atguigu.bridge.Sample;/*** 类的实现层次最上层*/
public abstract class DisplayImpl {public abstract void rawOpen();public abstract void rawPrint();public abstract void rawClose();
}

【类的实现层次结构:下层】

package com.atguigu.bridge.Sample;/*** 类的实现层次结构,具体实现类*/
public class StringDisplayImpl extends DisplayImpl {/*** 要显示的字符串*/private String string;/*** 以字节单位计算出的字符串的宽度*/private int width;/*** 构造方法** @param string 构造函数接收要显示的字符串string*/public StringDisplayImpl(String string) {// 将它保存在字段中this.string = string;// 把字符串的宽度也保存在字段中,以供使用this.width = string.getBytes().length;}public void rawOpen() {printLine();}public void rawPrint() {// 前后加上"|"并显示System.out.println("|" + string + "|");}public void rawClose() {printLine();}private void printLine() {// 显示用来表示方框的角的"+"System.out.print("+");// 显示width个"-"for (int i = 0; i < width; i++) {// 将其用作方框的边框System.out.print("-");                    }// 显示用来表示方框的角的"+"System.out.println("+");}
}

【主类】

package com.atguigu.bridge.Sample;public class Main {public static void main(String[] args) {Display d1 = new Display(new StringDisplayImpl("Hello, China."));Display d2 = new CountDisplay(new StringDisplayImpl("Hello, World."));CountDisplay d3 = new CountDisplay(new StringDisplayImpl("Hello, Universe."));d1.display();System.out.println();d2.display();System.out.println();d3.display();d3.multiDisplay(5);}
}

【运行】

+-------------+
|Hello, China.|
+-------------++-------------+
|Hello, World.|
+-------------++----------------+
|Hello, Universe.|
+----------------+
+----------------+
|Hello, Universe.|
|Hello, Universe.|
|Hello, Universe.|
|Hello, Universe.|
|Hello, Universe.|
+----------------+Process finished with exit code 0

拓展一

在上述示例程序中增加一个类,实现“显示字符串若干(随机)次”的功能。

【新增类】

package com.atguigu.bridge.A1;import java.util.Random;public class RandomCountDisplay extends CountDisplay {private Random random = new Random();public RandomCountDisplay(DisplayImpl impl) {super(impl);}public void randomDisplay(int times) {multiDisplay(random.nextInt(times));}
}

【主类】

package com.atguigu.bridge.A1;public class Main {public static void main(String[] args) {RandomCountDisplay d = new RandomCountDisplay(new StringDisplayImpl("Hello, China."));d.randomDisplay(10);}
}

扩展二

在上述示例程序中增加一个类,实现“显示文本文件的内容”的功能。

【新增类】

package com.atguigu.bridge.A2;import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;public class FileDisplayImpl extends DisplayImpl {private String filename;private BufferedReader reader;/*** 循环显示的极限(缓存大小限制)*/private final int MAX_READAHEAD_LIMIT = 4096;  public FileDisplayImpl(String filename) {this.filename = filename;}public void rawOpen() {try {reader = new BufferedReader(new FileReader(filename));reader.mark(MAX_READAHEAD_LIMIT);} catch (IOException e) {e.printStackTrace();}// 装饰框System.out.println("=-=-=-=-=-= " + filename + " =-=-=-=-=-="); }public void rawPrint() {try {String line;reader.reset(); // 回到mark的位置while ((line = reader.readLine()) != null) {System.out.println("> " + line);}} catch (IOException e) {e.printStackTrace();}}public void rawClose() {// 装饰框System.out.println("=-=-=-=-=-= "); try {reader.close();} catch (IOException e) {e.printStackTrace();}}
}

【主类】

package com.atguigu.bridge.A2;public class Main {public static void main(String[] args) {CountDisplay d = new CountDisplay(new FileDisplayImpl("star.txt"));d.multiDisplay(3);}
}

登场角色

  • Abstraction(抽象化):位于类的功能层次结构的最上层。它使用Implementor角色的方法定义了基本的功能。该角色中保存了Implementor 角色的实例
  • RefinedAbstraction(改善后的抽象化):在Abstraction角色的基础上增加了新功能
  • Implementor(实现者):位于“类的实现层次结构”的最上层。它定义了用于实现Abstraction 角色的接口(API)的方法
  • Concretelmplementor( 具体实现者):负责实现在Implementor 角色中定义的接口(API)

在这里插入图片描述

桥接模式在JDBC源码中的应用

在这里插入图片描述

总结

  • 实现了抽象和实现部分的分离,从而极大的提高了系统的灵活性,让抽象部分和实现部分独立开来,这有助于系统进行分层设计,从而产生更好的结构化系统
  • 分离之后代码的扩展性更强,当想要增加功能时,只需要在“类的功能层次结构”一侧增加类即可,不必对“类的实现层次结构”做任何修改。而且,增加后的功能可以被“所有的实现”使用(例如,我们可以将“类的功能层次结构”应用于软件所运行的操作系统上。如果我们将某个程序中依赖于操作系统的部分划分为 Windows版、Macintosh 版、Unix 版,那么我们就可以用 Bridge模式中的“类的实现层次结构”来表现这些依赖于操作系统的部分。也就是说,我们需要编写一个定义这些操作系统的共同接口(API)的Implementor角色,然后编写Windows版、Macintosh版Unix版的3个Concretelmplementor角色。这样一来,无论在“类的功能层次结构”中增加多少个功能,它们都可以工作于这3个操作系统上。)
  • 对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了,其它的部分由具体业务来完成
  • 桥接模式替代多层继承方案,可以减少子类的个数,降低系统的管理和维护成本
  • 桥接模式的引入增加了系统的理解和设计难度(较难分析出哪些是抽象层,哪些是实现层),由于聚合关联关系建立在抽象层要求开发者针对抽象进行设计和编程
  • 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围有一定的局限性,即需要有这样的应用场景

文章说明

  • 本文章为本人学习尚硅谷的学习笔记,文章中大部分内容来源于尚硅谷视频(点击学习尚硅谷相关课程),也有部分内容来自于自己的思考,发布文章是想帮助其他学习的人更方便地整理自己的笔记或者直接通过文章学习相关知识,如有侵权请联系删除,最后对尚硅谷的优质课程表示感谢。
  • 本人还同步阅读《图解设计模式》书籍(图解设计模式/(日)结城浩著;杨文轩译–北京:人民邮电出版社,2017.1),进而综合两者的内容,让知识点更加全面

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

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

相关文章

JAVA-字符串生成图片

直接上代码 public static void main(String[] args) throws IOException {createFontImage("红色", new Font("宋体", Font.BOLD, 50), 400, 400);}/*** 根据str,font的样式将文字变成图片,然后返回一个流** param str 字符串* param font 字体* pa…

YOLOv5多模型推理,同时实现多个任务数据集识别。

YOLOv5-6.0多模型推理,同时实现多个任务数据集识别。 ↓下滑可见代码↓ YOLOv5-6.0多模型推理,同时实现多个任务数据集识别。 ↓下滑可见代码↓ YOLOv5-6.0多模型推理,同时实现多个任务数据集识别。 ↓下滑可见代码↓ YOLOv5-6.0多模型推理,同时实现多个任务数据集识别。 ↓…

高阶k8s二次开发教程 -- 通过阅读Istio源码习得

本篇文章全网几乎找不到&#xff0c;在做深层次的k8s二次开发时非常管用。那就是使用Client-go去访问自定义CRD资源。 我们先使用kubebuilder生成一个CRD&#xff0c;论生成CRD这些&#xff0c;还是kubebuilder更加方便。 创建CRD apiVersion: "apiextensions.k8s.io/v…

了解Unity编辑器 之组件篇Effects(十一)

一、Halo&#xff1a;是一个可用于游戏对象的特效组件&#xff0c;它可以在对象周围添加一个光晕效果 Color属性: 用于设置Halo的颜色。你可以通过选择颜色面板中的颜色来指定光晕的外观。选择适当的颜色可以使光晕与游戏场景中的其他元素相匹配或突出显示。 Size属性: 用于设…

[SQL挖掘机] - 视图介绍

介绍: 视图&#xff08;View&#xff09;是数据库中的一种虚拟表格&#xff0c;它是基于一个或多个实际表格&#xff08;或其他视图&#xff09;的查询结果集合。与实际表格不同&#xff0c;视图不包含实际存储的数据&#xff0c;而是根据定义在其之上的查询语句来动态生成数据…

LeetCode|backtracking|review:40. 131. 93. 47. 332. | 37. Sudoku Solver

复习&#xff1a; 40. Combination Sum II [1,1,2,3]中&#xff0c;答案里有[1,1,2], 但是不能有两个[1,2,3] 131. Palindrome Partitioning 每个for都是在给定的start之后找一个palindrome。当start 93. Restore IP Addresses forloop每次loop都是在给定的start的后三个数…

javascript 模板引擎

使用场景 在实际开发中&#xff0c;一般都是使用动态请求数据来更新页面&#xff0c;服务器端通常返回json格式的数据&#xff0c;正常操作是我们手动的去拼装HTML&#xff0c;但麻烦且容易出错&#xff0c;因此出现了一些用模版生成HTML的的框架叫js模板引擎如&#xff1a;jq…

Sugar BI : AI 问答,即问即答

AI 探索功能提供给所有用户自由探索和分析数据模型的能力。在 AI 探索页中&#xff0c;有授权的用户可以通过 AI 问答和字段拖拽两种方式对数据模型进行探索。 下面&#xff0c;我们将为大家详细指导如何使用 AI 探索 新建 AI 探索页 空间管理员可以在报表管理中新建「AI 探索…

Docker 容器基础操作

Docker容器基础操作 容器(container)是Docker镜像的运行实例,类似于可执行文件与进程的关系,Docker是容器引擎,相当于系统平台。 容器的生命周期 容器的基础操作(以 tomcat8.0 为例) # 拉取tomcat8.0镜像 [root@tudou tudou]# docker pull tomcat:8.0 8.0: Pulling f…

紫光FPGA试用--软件篇

目录 一 软件安装启动 二 如何打开IP核&#xff1f;查看/修改现有IP核参数&#xff1f; 三 如何定义引脚&#xff1f; 四 如何下载code进入FPGA? 1. 下载到FPGA芯片内&#xff1a; 2.下载到外部FLASH中 五 如何进入在线调试模式&#xff0c;调试步骤 操作步骤&#xff…

智慧园区楼宇合集:数字孪生管控系统

智慧园区是指将物联网、大数据、人工智能等技术应用于传统建筑和基础设施&#xff0c;以实现对园区的全面监控、管理和服务的一种建筑形态。通过将园区内设备、设施和系统联网&#xff0c;实现数据的传输、共享和响应&#xff0c;提高园区的管理效率和运营效益&#xff0c;为居…

学习笔记|大模型优质Prompt开发与应用课(二)|第五节:只需3步,优质Prompt秒变应用软件

原作者&#xff1a;依依│百度飞桨产品经理 一乔│飞桨开发者技术专家 分享内容 01:大模型应用简介 02:LLM应用开发范式 03: Al Studio大模型社区 04:AI对话类应用开发技巧 大模型技术爆发&#xff0c;各类应用产品涌现 文心产业级知识增强大模型 工作中的“超级助手”—…

兵兵数码:网络机顶盒哪个好?2023最新网络机顶盒排名

网络机顶盒让电视机重生&#xff0c;解决卡顿、资源少、广告多等问题&#xff0c;我们每年都会进行网络机顶盒测评&#xff0c;今年已经测评过17款&#xff0c;通过多角度对比筛选了五款表现最佳的产品整理成网络机顶盒排名&#xff0c;近期想买网络机顶盒不知道网络机顶盒哪个…

Java lamda对List<JSONObject>里多个动态属性字段进行动态的降序或者升序

最近做到一个需求&#xff0c;需要把业务侧返回的数据&#xff08;格式为List<JSONObject>&#xff09;,然后根据前端传来的排序字段、以及升降序属性来排序并返回给前端。要对List<JSONObject>中的多个属性字段进行动态的升序或降序排序&#xff0c;我们可以根据需…

7.28黄金还会继续下跌吗?收官多空如何布局

近期有哪些消息面影响黄金走势&#xff1f;黄金多空该如何研判&#xff1f; ​黄金消息面解析&#xff1a; 周五&#xff08;7月28日&#xff09;亚洲时段&#xff0c;现货黄金震荡微涨&#xff0c;目前交投于1952.94附近&#xff0c;隔夜金价大跌后&#xff0c;吸引了一些逢低…

【深入了解pytorch】PyTorch循环神经网络(RNN)

【深入了解pytorch】PyTorch循环神经网络(RNN) PyTorch循环神经网络(RNN):概念、工作原理与常见变体循环神经网络概念和工作原理RNN的结构RNN的工作原理LSTM(长短期记忆网络)LSTM的结构LSTM的工作原理GRU(门控循环单元)GRU的结构GRU的工作原理在PyTorch中实现RNN、LST…

HTML 速查列表

HTML 速查列表 HTML 速查列表. 你可以打印它&#xff0c;以备日常使用。 HTML 基本文档 <!DOCTYPE html> <html> <head> <title>文档标题</title> </head> <body> 可见文本... </body> </html> 基本标签&#xff08;Ba…

使用python将PDF转word

实现功能&#xff0c;将程序所在当前路径下的所有PDF文件转化为word import os from pdf2docx import Converter# 获取当前路径 current_path os.getcwd()# 遍历当前路径下的所有文件和文件夹 for file_name in os.listdir(current_path):# 检查文件是否为 PDF 文件if file_n…

[SQL挖掘机] - 全连接: full join

介绍: 在sql中&#xff0c;join是将多个表中的数据按照一定条件进行关联的操作。全连接&#xff08;full join&#xff09;是一种连接类型&#xff0c;它会返回所有满足连接条件的行&#xff0c;同时还包括那些在左表和右表中没有匹配行的数据。 在进行全连接时&#xff0c;会…

Kotlin Multiplatform 使用测试单元

编写常见的测试代码 现在您有了一个基于字符串的 API&#xff0c;可以通过它进行基本的测试。 1、在公共的测试模块中创建一个 org.jetbrains.base64 包 2、在新包下面创建 Base64Test.kt文件 3、在文件中添加代码 package org.jetbrains.base64import com.example.myapplicat…