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

什么是桥接模式?一文详解

目录

一.意图

二.结构

三.举例:产品上色

1.不用桥接模式的场景

2.使用桥接模式的场景

3.代码实现

①创建“抽象产品”(本质是一个抽象类)

②创建“抽象颜色”(本质是一个接口)

③创建两个具体的产品,比如产品A、产品B

④创建三个具体的颜色,比如红、蓝、绿

最终,进行测试

4.总结

四.软考真题

1.2009年上半年

题目

分析过程

正确答案

2.2013年下半年

题目

分析过程

正确答案

3.2017年下半年

题目

分析过程

正确答案


一.意图

将抽象部分与实现部分分离,使它们都可以独立地变化。

二.结构

下图只是一个大致的、通用的一个示意结构图,看起来懵是正常的,因此我们下面会举具体的例子。

三.举例:产品上色

我们就拿产品(比如产品需要根据具体情况上色)来举例。

说白了就是,给产品上色时,受两种因素影响:

  • 产品的类型(即:产品A、产品B、产品C之间,它们由于物理结构不同,上色工艺就不同)
  • 产品的颜色(即:红色的产品A就上红色,蓝色的产品A就上蓝色)

1.不用桥接模式的场景

如下图,可见此时类的数量就会爆炸(即需要创建很多的类)。

一共需要创建类的数量 = 1 + m + m*n

  • 1就是“抽象产品”(第一层)
  • m就是产品A、产品B.....这些(第二层)
  • m*n就是带颜色的产品的数量(即有m种产品,每种产品又有n种颜色)

思考:为什么会类的数量爆炸?

答案:因为颜色是在产品类中的,应该单独提取成“抽象颜色”这个模块,然后再与“抽象产品”进行一个关联(即桥接模式,这个关联就相当于一个桥)。

2.使用桥接模式的场景

如下图,此时类的数量就会大大减少

一共需要创建类的数量 = 1 + m + 1 +n

  • 1就是“抽象产品”
  • m就是产品A、产品B....这些
  • 1就是“抽象颜色”
  • n就是红、蓝、绿.....这些

注意:菱形表示“抽象颜色”属于“抽象产品”的一部分,说白了就是“抽象产品”这个抽象类中,要定义一个属性,即“抽象颜色”。

注意:

“抽象颜色”这个抽象,用抽象类、接口来实现都可以,因为这俩都是抽象的东西,具体采用哪个,就需要具体情况具体分析。

3.代码实现

①创建“抽象产品”(本质是一个抽象类)

//抽象产品 abstract class Product { //属性 private String type;//产品类型:比如产品A、产品B、产品C protected Color color;//颜色:比如红、蓝、绿 //方法 public abstract void Operation();//上色方法 //get、set public String getType() { return type; } public void setType(String type) { this.type = type; } public Color getColor() { return color; } public void setColor(Color color) { this.color = color; } }

②创建“抽象颜色”(本质是一个接口)

//抽象颜色 public interface Color { //根据“产品的类型”上色,具体上什么颜色,由Color接口的具体实现类决定 public void OperationImp(String productType); }

③创建两个具体的产品,比如产品A、产品B

public class ProductA extends Product{ @Override //实现父类Product定义的抽象方法(在Product中,抽象方法没有方法体,所以在这里给出) public void Operation() { //调用上色方法 color.OperationImp(this.getType());//color继承自父类的属性color ,this代表ProductA的对象本身 } }
public class ProductB extends Product{ @Override //实现父类Product定义的抽象方法(在Product中,抽象方法没有方法体,所以在这里给出) public void Operation() { //调用上色方法 color.OperationImp(this.getType());//color继承自父类的属性color ,this代表ProductB的对象本身 } }

④创建三个具体的颜色,比如红、蓝、绿

public class Red implements Color{ @Override //实现接口Color中定义的上色方法 public void OperationImp(String productType) { System.out.println("正在给"+productType+"涂上红色"); } }
public class Blue implements Color{ @Override //实现接口Color中定义的上色方法 public void OperationImp(String productType) { System.out.println("正在给"+productType+"涂上蓝色"); } }
public class Green implements Color{ @Override //实现接口Color中定义的上色方法 public void OperationImp(String productType) { System.out.println("正在给"+productType+"涂上绿色"); } }

最终,进行测试

测试1:先来个红色的产品A

public class Demo01Application { public static void main(String[] args) { //目标:此时我想要一个红色的产品A //1、创建一个具体的产品(比如产品A,即类型为A的产品) ProductA productA = new ProductA(); //2、初始化产品的两个属性:产品类型、颜色(这是影响产品上色的两个因素) productA.setType("产品A"); Color color = new Red();//这就是泛型(说白了就是左边是通用的接口,右边是具体的实现类) productA.setColor(color); //3、执行上色方法 productA.Operation(); } }

运行效果如下:

测试2:再来一个蓝色的产品B

public class Demo01Application { public static void main(String[] args) { //目标:此时我想要一个蓝色的产品B //1、创建一个具体的产品(比如产品B,即类型为B的产品) ProductB productB = new ProductB(); //2、初始化产品的两个属性:产品类型、颜色(这是影响产品上色的两个因素) productB.setType("产品B"); Color color = new Blue(); productB.setColor(color);//这就是泛型(说白了就是左边是通用的接口,右边是具体的实现类) //3、执行上色方法 productB.Operation(); } }

运行效果如下:

4.总结

  • 颜色本身就是属于产品的一个属性,但是由于我们产品有很多类型,每个类型又有很多颜色。当我们的上色工艺,是由产品类型 + 颜色这两个因素决定的,此时我们就需要创建很多类(因为红色的产品A、红色的产品B、蓝色的产品C、绿色的产品D等等,它们的上色工艺都不同,所以只要产品类型 or 颜色 有一个不同,就需要定义一个专门的上色方法),因此此时就需要创建很多个类(因为是相乘关系,即产品类型数量 * 颜色数量)
  • 此时为了解决这个问题,我们可以将颜色单独抽出为“抽象颜色”color(是一个接口),接口中定义一个上色方法(参数为“产品类型”),然后将这个color接口当做“抽象产品”的一个属性。此时我们可以定义很多颜色类去实现这个color接口,每个颜色类,根据接口的产品类型,再结合上自己的颜色,最终写出来独特的上色工艺(由产品类型+颜色决定)
  • 这样一看,核心思路就是,将颜色单独抽象为“抽象颜色”color接口,然后又把这个color接口当做“抽象产品”的一个属性。这就相当于一个桥梁,也是桥接模式的灵魂,可见十分灵活。
  • 而且要体会泛型的思想,代码如下:

左面的Color是接口,右边是Color接口的具体实现类。说白了就是父 = 子 ,这种就是泛型的思想。也只有这样,我们才能把这个color塞到“抽象产品”中当做属性。你要是不用泛型,而是Blue blue = new Blue(); 将blue塞到“抽象产品”中当做属性,会报错,因为“抽象产品”类中,属性定义的是Color接口。

四.软考真题

1.2009年上半年

题目

分析过程

(1)this.imp

但注意,要用this关键字(this表示当前类的本身)区分开imp是属性还是形参。

因此第一空答案为:this.imp


(2)ImageImp

故第二空答案为:ImageImp


(3)imp.doPaint(m)

此时我们可见,显示像素矩阵m的方法在抽象类ImageImp中,此时BMP对象如何获取?

别忘了,我们BMP继承了Image类,Image类中就有,即属性imp。

因此,此时这个BMP类,可以直接使用父类对象imp,去调用显示像素矩阵的方法。

因此第三空的答案为:imp.doPaint(m)


(4)new BMP()

根据泛型的思想,再结合桥接模式图,可轻松得到答案:

因为人家说要查看bmp类型的图像文件,所以此时要创建BMP对象

故第四空答案为:new BMP()


(5)new WinImp()

思考过程同上,人家要在Windows操作系统上查看,因此要创建WinImp类的对象

(6)image1.setImp(imageImp1)

由于Image类需要ImageImp当做属性,因此很清晰就能得到这一空的答案。

(7)17

  • Image占一个
  • ImageImp占一个
  • BMP、GIF等等,共有10个(题目给的)
  • WinImp、LinuxImp,共有5个

综上,一共1+1+10+5 = 17个

正确答案

可见,我们的上述答案,和下面的正确答案一样。

2.2013年下半年

题目

分析过程

(1)interface

我们往后看,发现都有的类实现了Drawing了,那Drawing肯定就是接口了。

故第一空的答案为:interface


(2)(3)

具体定义了哪两个方法,可以去看一下它的实现类,如下:

因此可见:

  • 第二空的答案为:public void drawLine(double x1, double y1,double x2, double y2)
  • 第三空的答案为:public void drawCircle(double x, double y, double r)

(4)(5)

然后再结合这个规则,我们还需要去找一下具体的方法在哪里:

  • 第四空的答案为:DP1.draw_a_circle(x, y, r)
  • 第五空的答案为:DP2.drawcircle(x, y, r)

(6)

并且,这个draw方法被子类又实现了一遍,并且Shape本身就是抽象类,所以draw方法肯定是抽象的(需要用abstract修饰),且需要子类能访问,故还要用public修饰。通过子类能发现draw方法的返回值是void

因此第六空的答案为:abstract public void draw()

正确答案

经过对比,发现我们上述的答案和正确答案一致。(下面的答案没写public关键字,最好带上)

3.2017年下半年

题目

分析过程

(1)abstract void doPaint(Matrix m)

由于Implementor是抽象类、且该方法没有方法体,故这个方法肯定是abstract修饰的。再根据该抽象类的两个子类,可得该方法的方法名为doPaint,且返回值为void,形参为Matrix m

因此第一空的答案为:abstract void doPaint(Matrix m)


(2)imp.doPaint(m)

由于GIFImage继承了Image类,所以就有imp这个属性了(是Implementor抽象类的对象),因此就可以调用其中的doPaint方法来显示像素矩阵了。

故第二空的答案为:imp.doPaint(m)


(3)(4)(5)

由于人家要看gif类型的图像,所以我们要创建GIFImage类的对象。而且人家要在linux操作系统上查看,因此我们必须创建LinuxImp类的对象。最后再将imageImp放入image中充当属性。故:

  • 第三空的答案:new GIFImage()
  • 第四空的答案:new LinuxImp()
  • 第五空的答案:image.setImp(imageImp)

正确答案

将上述我们的答案,和下面的正确答案对比,发现全对。

http://www.jsqmd.com/news/831137/

相关文章:

  • Verilog实现1位半加器与全加器:从逻辑门到模块化设计
  • ARM GIC寄存器架构与虚拟化中断管理详解
  • CircuitPython嵌入式开发实战:从文件系统损坏到硬件兼容性的全面故障排查指南
  • 基于 HarmonyOS 6.0 的跨端应用页面开发实践:ProfilePage 构建与深度解析
  • J公司邯郸主城区配送系统优化【附代码】
  • 点云配准零件三维缺陷检测【附代码】
  • 观察使用Taotoken后项目月度大模型API成本的变化情况
  • Mac Mouse Fix终极问题解决指南:让你的普通鼠标比苹果触控板更好用
  • DPDK TestPMD实战:如何用多核配置压测出万兆网卡的真实转发性能?
  • 20260516 之所思 - 人生如梦
  • Live Server架构深度解析:构建高效前端开发环境的技术实现
  • 终极指南:5步彻底解决Gopeed下载管理器403 Forbidden错误
  • 免支撑3D打印:为Adafruit FunHouse打造专属复古砖纹支架
  • 自主Agent时代的Harness Engineering:如何管控超自动化的Agent行为
  • 面试必问的建立/保持时间(tSU/tH)到底是什么?从钟控D锁存器动态参数讲透时序分析
  • LAMMPS分子动力学模拟:3小时掌握大规模原子并行计算完整指南
  • 5分钟让AI分析你的阅读人格,微信读书这个Skill太准了!
  • RL78/G13驱动多位数码管:74HC573动态扫描方案详解
  • Eagle元器件库创建全攻略:从封装、符号到设备集成的硬件设计基石
  • 深度学习篇---向量空间
  • 别再死记硬背了!用Python代码动画演示组合数11个核心性质(附推导过程)
  • 高速PCB设计中的信号完整性分析与优化实践
  • 用MATLAB和FPGA手把手仿真DMTD相位噪声测量(附源码与避坑指南)
  • UltimateStack:终极解决方案!突破Minecraft物品堆叠限制的完整指南
  • 卫星拒止条件车辆定位系统设计【附方案】
  • 告别U盘!用PXE网络批量装UOS,一台电脑搞定所有(附Arm/Mips/X86全架构配置)
  • GD32F103C8T6 I2C实战:用两块板子互发数据,手把手调试SBSEND、ADDSEND这些关键状态位
  • OpenClaw用户如何快速接入Taotoken扩展Agent能力
  • 打卡信奥刷题(3271)用C++实现信奥题 P8855 [POI 2002 R1] 商务旅行
  • 【职场】工作中当我说“好的,收到“,我说的是……