桥接模式

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

1.简介

  • 扩展性问题(类爆炸),如果我们再增加手机的样式(旋转式),就需要增加各个品牌手机的类,同样如果我们增加一个手机品牌,也要在各个手机样式类下增加。

  • 违反了单一职责原则,当我们增加手机样式时,要同时增加所有品牌的手机,这样增加了代码维护成本.

  • 解决方案-使用桥接模式

桥接模式(Bridge)-基本介绍基本介绍

桥接模式(Bridge模式)是指:将实现与抽象放在两个不同的类层次中,使两个层次可以独立改变。
是一种结构型设计模式
Bridge模式基于类的最小设计原则,通过使用封装、聚合及继承等行为让不同的类承担不同的职责。它的主要特点是把抽象(Abstraction)与行为实现(Implementation)分离开来,从而可以保持各部分的独立性以及应对他们的功能扩展

image-20210305221204784

  • Client类:桥接模式的调用者
  • 抽象类(Abstraction) :维护了lmplementor/即它的实现类ConcretelmplementorA…二者是聚合关系, Abstraction充当桥接类
  • RefinedAbstraction :是Abstraction抽象类的子类
  • lmplementor:行为实现类的接口
  • ConcreteImplementorA/B :行为的具体实现类
  • 从UML图:这里的抽象类和接口是聚合的关系,其实调用和被调用关系

2.案例

1
2
3
4
5
6
public interface Brand {

void open();
void close();
void call();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class XiaoMi implements Brand{
@Override
public void open() {
System.out.println("小米手机开机");
}

@Override
public void close() {
System.out.println("小米手机关机");
}

@Override
public void call() {
System.out.println("小米手机打电话");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Vivo implements Brand {

@Override
public void open() {
System.out.println("Vivo手机开机");
}

@Override
public void close() {
System.out.println("Vivo手机关机");
}

@Override
public void call() {
System.out.println("Vivo手机打电话");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public abstract class Phone {

private Brand brand;

//构造器
public Phone(Brand brand) {
this.brand = brand;
}

protected void open(){
this.brand.open();
}

protected void close(){
this.brand.close();
}

protected void call(){
this.brand.call();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class FoldPhone extends Phone {

public FoldPhone(Brand brand) {
super(brand);
}

public void open(){
super.open();
System.out.println("折叠样式的手机");
}

public void close(){
super.close();
System.out.println("折叠样式的手机");
}

public void call(){
super.call();
System.out.println("折叠样式的手机");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Client {

public static void main(String[] args) {
//获取折叠式手机(样式+品牌)
FoldPhone phone = new FoldPhone(new XiaoMi());
phone.open();
phone.call();
phone.close();
}
}
/*
小米手机开机
折叠样式的手机
小米手机打电话
折叠样式的手机
小米手机关机
折叠样式的手机
*/

Phone充当了桥的功能 让具体的手机类型去掉用品牌 实际上实现的功能

减少拓展时需要增加的类

类似数据库的多对多

3,.桥接模式在JDBC的源码剖析桥接模式在

JDBC的源码剖析

Jdbc的 Driver接口,如果从桥接模式来看,Driver就是一个接口,下面可以有MySQL的Driver,Oracle的Driver,这些就可以当做实现接口类
代码分析+Debug源码

image-20210305223924937

可以看到需要返回的是Connection对象。在Java中通过Connection提供给各个数据库一样的操作接口,这里的Connection可以看作抽象类。可以说我们用来操作不同数据库的方法都是相同的,不过MySQL有自己的ConnectionImpl类,同样Oracle也有对应的实现类。这里Driver和Connection之间是通过DriverManager类进行桥接的,不是像我们上面说的那样用组合关系来进行桥接。

3、桥接模式优缺点

3.1 优点

抽象和实现的分离。
优秀的扩展能力。
实现细节对客户透明。
经常遇到一些可以通过两个或多个维度划分的事物,第一种解决方式就是多层继承,但是复用性比较差,同时类的个数也会很多,桥接模式是改进其的更好办法
桥接模式增强了系统的扩展性,在两个维度中扩展任意一个维度都不需要修改原有代码,符合开闭原则

3.2 缺点

桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
桥接模式增加了系统的理解与设计难度:因为聚合关系建立在抽象层,要求开发者针对抽象化进行设计与编程,能正确地识别出系统中两个独立变化的维度

3.3 使用场景

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

3.4 注意事项

注意事项:对于两个独立变化的维度,使用桥接模式再适合不过了。

4、桥接模式与适配器模式

适配器模式与桥接模式的区别和联系

适配器模式和桥接模式都是间接引用对象,因此可以使系统更灵活,在实现上都涉及从自身以外的一个接口向被引用的对象发出请求。

两种模式的区别在于使用场合的不同,适配器模式主要解决两个已经有接口间的匹配问题,这种情况下被适配的接口的实现往往是一个黑匣子。我们不想,也不能修改这个接口及其实现。同时也不可能控制其演化,只要相关的对象能与系统定义的接口协同工作即可。适配器模式经常被用在与第三方产品的功能集成上,采用该模式适应新类型的增加的方式是开发针对这个类型的适配器,如下图所示:

img

桥接模式则不同,参与桥接的接口是稳定的,用户可以扩展和修改桥接中的类,但是不能改变接口。桥接模式通过接口继承实现或者类继承实现功能扩展。如下图所示:

img

桥接模式和适配器模式用于设计的不同阶段,桥接模式用于设计的前期,即在设计类时将类规划为逻辑和实现两个大类,是他们可以分别精心演化;而适配器模式用于设计完成之后,当发现设计完成的类无法协同工作时,可以采用适配器模式。然而很多情况下在设计初期就要考虑适配器模式的使用,如涉及到大量第三方应用接口的情况。

5.使用场景

JDBC 驱动程序

银行转账系统
转账分类:网上转账,柜台转账,AMT转账
转账用户类型:普通用户,银卡用户,金卡用户..

消息管理
消息类型:即时消息,延时消息
消息分类:手机短信,邮件消息,QQ消息…