一 概述

桥接模式在日常开发中不是特别常用,主要是因为上手难度较大,但是对于理解面向对象设计有非常大的帮助。

1.1 定义

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

  • 桥接模式属于结构型模式

举个生活中的例子,一条数据线,一头 USB 接口的可以连接电脑、充电宝等等,另一头可以连接不同品牌的手机,通过这条数据线,两头不同的东西就可以连接起来,这就是桥接模式。

1.2 使用场景

桥我们大家都熟悉,顾名思义就是用来将河的两岸联系起来的。而此处的桥是用来将两个独立的结构联系起来,而这两个被联系起来的结构可以独立的变化,所以其它的理解只要建立在这个层面上就会比较容易。

下面是一些官方的说明,比较晦涩,必须等你有一定的经验后才能理解:

  • 如果一个系统需要在抽象化和具体化之间增加更多的灵活性,避免在两个层次之间建立静态的继承关系,通过桥接模式可以使它们在抽象层建立一个关联关系
  • “抽象部分”和“实现部分”可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将一个抽象化子类的对象和一个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合
  • 一个类存在两个(或多个)独立变化的维度,且这两个(或多个)维度都需要独立进行扩展
  • 对于那些不希望使用继承或因为多层继承导致系统类的个数急剧增加的系统,桥接模式尤为适用

1.3 UML 类图


角色说明:

  • Abstraction (抽象化角色):一般是抽象类,定义该角色的行为,同时保存一个对实现化角色的引用
  • Implementor (实现化角色):接口或者抽象类,定义角色必需的行为和属性
  • ConcreteImplementorA、ConcreteImplementorB (具体实现化角色):实现角色的具体行为

1.4 举例

在讲策略模式的时候,王二狗和牛翠花不是要到天津之眼去约会,两人到那后先去星巴克喝咖啡了,星巴克提供了多种选择,从容量上说有大杯,中杯,小杯,从口味上说有原味,加糖。这可难为了有选择恐惧症的牛翠花,半天点不出来,后面的人都开始骂娘了,王二狗也只能陪着笑脸道歉。

其实被难为的除了牛翠花,还有给星巴克做订单系统的外包公司的程序员林蛋。星巴克一开始提需求的时候只有正常杯(中杯),原味和加糖这几种选择,人家林蛋也是有两年工作经验的码农,这需求不在话下,林蛋还想到了要面向抽象编程。

首先定义一个点咖啡接口,里面有一个下单方法,至于点哪种口味的咖啡,就由其子类去决定,完美。

public interface ICoffee {void orderCoffee(int count);
}

原味咖啡类

public class CoffeeOriginal implements ICoffee {@Overridepublic void orderCoffee(int count) {System.out.println(String.format("原味咖啡%d杯",count));}
}

加糖咖啡类

public class CoffeeWithSugar implements ICoffee {@Overridepublic void orderCoffee(int count) {System.out.println(String.format("加糖咖啡%d杯",count));}
}

搞定收工,王者荣耀搞起!项目经理突然过来了:林蛋啊,客户那边说了,他们准备增加两个容量规格的咖啡,大杯和小杯,你改一下。林蛋:what?尼玛为什么不早说,老子刚写完。胸中虽有万千牢骚,还得平复一下心情去改代码,何必呢?林蛋心中暗喜,幸好老子是面向抽象编程的,对应加几个实现类不就得了?

//中杯加糖
public class CoffeeWithSugar implements ICoffee {@Overridepublic void orderCoffee(int count) {System.out.println(String.format("中杯加糖咖啡%d杯",count));}
}
//大杯加糖
public class LargeCoffeeWithSugar implements ICoffee {@Overridepublic void orderCoffee(int count) {System.out.println(String.format("大杯加糖咖啡%d杯",count));}
}
//小杯加糖
public class SamllCoffeeWithSugar implements ICoffee {@Overridepublic void orderCoffee(int count) {System.out.println(String.format("小杯加糖咖啡%d杯",count));}
}
...

加着加着林蛋慌了,共需要 3x2=6 个类啊,大杯原味和加糖,中杯原味和加糖,小杯原味和加糖。万一过段时间那 2B 客户又要出加奶,加蜂蜜等等口味,说不定还有迷你杯,女神杯等等规格的咖啡,那我这边的类不就爆炸了吗?看来得翻翻书去找个设计模式了。。。

此场景桥接模式正合适,这里有两个变化维度,咖啡的容量和口味,而且都需要独立变化。如果使用继承的方式,随着变化类就会急剧的增加。你可以将容量理解为抽象部分,而口味理解为实现部分,这两个部分需要桥接。

二 实现

可以将咖啡的容量作为抽象化 Abstraction,而咖啡口味为实现化 Implementor。

2.1 创建实现化部分的接口定义(咖啡口味的维度)

public interface ICoffeeAdditives {void addSomething();
}

2.2 创建抽象化部分的接口定义(咖啡容量的维度)

// 抽象化 Abstraction
public abstract class Coffee {protected ICoffeeAdditives additives;public Coffee(ICoffeeAdditives additives) {this.additives=additives;}public abstract void orderCoffee(int count);
}

我们可以看到,Coffee 持有了 ICoffeeAdditives 的引用,ICoffeeAdditives 的实例是通过构造函数注入的,这个过程就是我们所说的桥接过程。我们通过这个引用就可以调用 ICoffeeAdditives 的方法,进而将 Coffee 的行为与 ICoffeeAdditives 的行为通过 orderCoffee() 方法而组合起来。

下面是一个对抽象化修正的一个类,林蛋在里面增加了一个用于品控的方法:

// 抽象化的修正类
public abstract class RefinedCoffee extends Coffee {public RefinedCoffee(ICoffeeAdditives additives) {super(additives);}public void checkQuality(){Random ran=new Random();System.out.println(String.format("%s 添加%s",additives.getClass().getSimpleName(),ran.nextBoolean()?"太多":"正常"));}
}

2.3 实现实现化部分(咖啡口味维度)的接口 ICoffeeAdditives

// 加奶
public class Milk implements ICoffeeAdditives {@Overridepublic void addSomething() {System.out.println("加奶");}
}
// 加糖
public class Sugar implements ICoffeeAdditives {@Overridepublic void addSomething() {System.out.println("加糖");}
}

2.4 实现抽象化部分(咖啡的容量维度)的接口Coffee

// 大杯
public class LargeCoffee extends RefinedCoffee {public LargeCoffee(ICoffeeAdditives additives) {super(additives);}@Overridepublic void orderCoffee(int count) {additives.addSomething();System.out.println(String.format("大杯咖啡%d杯",count));}
}
// 小杯
public class SmallCoffee extends RefinedCoffee {public LargeCoffee(ICoffeeAdditives additives) {super(additives);}@Overridepublic void orderCoffee(int count) {additives.addSomething();System.out.println(String.format("小杯咖啡%d杯",count));}
}
// 中杯
...

2.5 客户端测试

    public static void main(String[] args) {//点两杯加奶的大杯咖啡RefinedCoffee largeWithMilk=new LargeCoffee(new Milk());largeWithMilk.orderCoffee(2);largeWithMilk.checkQuality();}

输出结果:

加奶
大杯咖啡2杯
Milk 添加太多

2.6 其他说明:

通过使用桥接模式,就使得咖啡的容量和口味这两个维度可以独立变化,互不干扰。原本需要 3x3=9 个类的功能,现在只需要3+3=6 个类,有的同学就要说了,我去,加上那些基类和原来也差不多,没有多好啊?别急,等来了新需求你就知道哪个好了。

新需求:增加一种加蜂蜜的口味,现在不使用桥接模式的类的个数是 3x4=12,而使用了的是 3+4=7,你说哪个好。

林蛋终于完成了代码的重构,回头一看王者荣耀由于恶意挂机被扣除15个信用分,暂时不能进行排位赛,无奈只能再去巩固一下桥接模式了。

三 总结

3.1 应用场景及特点

  • 一个类存在两个或以上的独立维度的变化,且这些维度都需要进行拓展
  • 不希望使用继承或因为多层次继承导致类的个数急剧增加时
  • 如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承关系,可以通过桥接模式使他们在抽象层建立一个关联关系

3.2 优点

  • 分离抽象接口及其实现部分。桥接模式使用“对象间的关联关系”解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。所谓抽象和实现沿着各自维度的变化,也就是说抽象和实现不再在同一个继承层次结构中,而是“子类化”它们,使它们各自都具有自己的子类,以便任何组合子类,从而获得多维度组合对象
  • 在很多情况下,桥接模式可以取代多层继承方案,多层继承方案违背了“单一职责原则”,复用性较差,且类的个数非常多,桥接模式是比多层继承方案更好的解决方法,它极大减少了子类的个数
  • 桥接模式提高了系统的可扩展性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统,符合“开闭原则”

3.3 缺点

  • 桥接模式的使用会增加系统的理解与设计难度,由于关联关系建立在抽象层,要求开发者一开始就针对抽象层进行设计与编程
  • 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性,如何正确识别两个独立维度也需要一定的经验积累

四 Android 中的源码实例分析

桥接模式在 Android 中的源码应用还是非常广泛的。比如 AbsListView 跟 ListAdapter 之间就是一个桥接模式。

4.1 AbsListView 与 ListAdapter 之间的桥接模式

相关代码就不贴了,看下它们的 UML 类图就明白了。


这里 AbsListView 是抽象化角色,ListAdapter 则是实现化角色。

4.2 其它

另外,Window 与 WindowManager 之间也是桥接模式。有兴趣的可以去看下源码体会体会。

JAVA设计模式之桥接模式相关推荐

  1. java 设计模式之桥接模式,策略模式

    java 设计模式之桥接模式,策略模式 1.引出设计模式 相信大家都玩过王者荣耀这款游戏.我们知道现在大概有九十多个英雄且各自技能及背景故事.台词.被动都不一样而且还带着召唤师技能比如实现,惩戒,弱化 ...

  2. 【Java设计模式】桥接模式

    ✍ 在介绍桥接模式之前,我们引入一个场景: 设想如果要绘制矩形.圆形.椭圆.正方形,我们至少需要4 个形状类,但是如果绘制的图形需要具有不同的颜色,如红色.绿色.蓝色等,此时至少有如下两种设计方案: ...

  3. Java设计模式:桥接模式

    一.什么是桥接模式 桥接(Bridge)模式属于结构型设计模式.通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦.把抽象(abstraction)与行为实现(implementation)分离开 ...

  4. (设计模式十)java设计模式之桥接模式

    桥接模式比较抽象,难理解,小哥看了很多其他博主的资料,看到一篇不错,转载一下. 转载请标明出处:https://blog.csdn.net/u013256816/article/details/510 ...

  5. 八戒转世投胎竟然是Java设计模式:桥接模式

    桥接模式 示例 代码实例 桥接模式 定义 意图 主要解决问题 何时使用 优缺点 八戒转世投胎的故事 示例 请开发一个画图程序,可以画各种颜色不同形状的图形,请用面向对象的思 想设计图形 分析: 1.比 ...

  6. Java设计模式(建造者模式-适配器模式-桥接模式)

    Java设计模式Ⅲ 1.建造者模式 1.1 建造者模式概述 1.2 建造者模式的注意事项和细节 1.3 代码理解 2.适配器模式 2.1 类适配器模式 2.1.1 代码理解 2.2 对象适配器模式 2 ...

  7. java桥接和装饰_设计模式:桥接模式和装饰模式

    原标题:设计模式:桥接模式和装饰模式 一.桥接模式简介 1.基础描述 桥梁模式是对象的结构模式.又称为柄体(Handle and Body)模式或接口(Interface)模式.桥梁模式的用意是&qu ...

  8. java 懒加载模式_JavaScript面试系列:JavaScript设计模式之桥接模式和懒加载

    我写的程序员面试系列文章 Java面试系列-webapp文件夹和WebContent文件夹的区别? 程序员面试系列:Spring MVC能响应HTTP请求的原因? Java程序员面试系列-什么是Jav ...

  9. Java设计模式 -10- 装饰器模式(Decorator模式)

    Java设计模式 -10- 装饰器模式(Decorator模式) 前言 装饰器模式的定义与特点 优点: 缺点: 装饰器模式的结构与实现 1. 模式的结构 2. 模式的实现 装饰器模式的应用实例 装饰器 ...

  10. 如何让孩子爱上设计模式 ——10.桥接模式(Bridge Pattern)

    如何让孩子爱上设计模式 --10.桥接模式(Bridge Pattern) 标签: 设计模式初涉 我有故事,你有酒吗?这年头写个技术文不讲个故事都不行,行,我讲: 还有发现很多的技术博文都开始有喜欢往 ...

最新文章

  1. Spring Data JPA 复杂/多条件组合分页查询
  2. matlab 离散点差值,散点图的插值方法
  3. 卡尔蔡司携手神策数据,赋能近视防控数字化
  4. html css入门经典 pdf,CSS入门经典
  5. js 中的console.log有什么作用
  6. 通过C学Python(3)列表的常用方法
  7. oracle 虚拟补丁,趋势科技虚拟补丁(Virtual Patch)
  8. 【转】Ubuntu中SVN客户端安装+使用
  9. 任意阶魔方阵matlab程序,【精品】任意阶魔方阵算法(c语言)
  10. html5是未来,开始用吧!
  11. ssis 包配置组织程序_如何停止失控的SSIS程序包
  12. 尝鲜 Svelte 前端框架,开发读书笔记
  13. 如何使用Intellij IDEA工具导入SVN项目
  14. tiledmap 图块属性_TiledMap详解
  15. 解决方法:未能加载文件或程序集“Microsoft.Office.Interop.Excel。。
  16. krpano JS动态切换旋转角
  17. Max函数、Min函数
  18. Javascript算法练习(四)
  19. 基于 CSS3 的下一代 Web 应用开发,第 1 部分: 发展历史及新特性
  20. 【脚本小子狂喜】日常实用脚本推荐

热门文章

  1. python中while循环只能用来实现无限循环的编程_while循环只能实现无限循环的编程...
  2. 如何快速使用计算机键盘,怎么使用电脑键盘快速打字
  3. java解惑之最后的笑声
  4. 机器学习基础:台大李宏毅的线性代数视频公开课
  5. 手把手教你完成unity3D跑酷游戏系列(二)
  6. Unity移动端、WebGL 四边形线框Shader 实现
  7. 从项目中由浅入深的学习vue,react,微信小程序和快应用(1)
  8. FJUT 3097(hdu 3333) 区间种类数 主席树+在线
  9. 街头霸王全人物故事背景
  10. python爬取晋江文学城_晋江文学城[本站宗旨]