一、概念
1、定义:将抽象部分和具体实现部分分离,使他们都可以独立的变化(即在一定程度上解耦)。桥接模式是通过组合的方式建立两个类之间的关系,而不是继承。
- 抽象部分:需要经过抽象化 ,忽略一些信息,把不同的实体当作同样的实体对待,抽取类的共同性质形成类,这个过程就叫抽象化的过程。抽象一般指抽象类。
- 具体实现部分:实现化,产生的对象比抽象部分产生的对象更为具体。具体的实现一般都是行为。
2、类型:结构型(Bridge)
3、适用场景
- 抽象和具体实现之间需要增加更多灵活性的时候,需要使用它,避免了在这两个层次之间建立静态的继承关系,通过桥接模式建立一层关联关系,抽象部分和具体的实现部分都是可以以继承的方式独立扩展,互不影响,就可以动态的将抽象部分子类的对象和具体实现部分子类的对象组合起来,这样就解耦了。
- 一个类存在两个(或多个)独立变化的维度,且这两个(或多个)维度需要独立进行扩展,也就是说抽象部分可以独立扩展,具体实现部分也可以独立扩展
- 不希望使用继承的情况下。因为多层继承导致系统类的个数剧增
4、优缺点
- 优点:
- 分离了抽象部分和具体实现部分,解耦了它们之间固定的绑定关系,使得它们不在同一个继承结构层次中,从而通过组合来获得多维度的组合对象。
- 提高了系统的可扩展性(扩展任意一个维度都不需要修改原有的系统)
- 符合OCP
- 符合合成复用原则
- 缺点:
- 增加了系统的理解和设计难度,编码时一开始就要针对抽象层去进行设计
- 需要开发人员正确识别出系统中两个独立变化的维度
5、相关设计模式
-
桥接与组合模式:组合模式强调的是部分和整体间的组合,桥接强调平行级别上不同类的组合。
-
桥接与适配器模式:共同点是桥接和适配器都是为了让两个东西配合工作,桥接是分离抽象和具体,目的是分离,适配器是适配功能相似,但是接口不一样的东西适配起来。
6、UML图
二、Coding
场景:中国有很多银行,农业银行(ABC Bank)、工商银行(ICBC Bank),我们也有自己的账号,分为定期账号、活期账号。分两块,一块一行,一块账号。
首先创建Account
接口,他是这个桥的具体实现层,有两个具体的实现类,分别是定期和活期账号,右侧是抽象层,创建了一个抽象类Bank,用的是Account
这个接口,这个是桥接模式的核心,注意这里是组合关系(实心菱形),一个Bank
中含有一个Account
,下面的ICBCBank
和ABCBank
是抽象类的具体的类实现,通过桥接模式把两者桥接起来,我们这所有说是具体是具体的Account
实现类,而抽象是这个Bank
。两端可以各自独立发展。
具体端:
//有两个银行就有两个账号,每个银行活期、定期,就有4种账号组合类型
//银行不断扩展,账号也不断扩展,使用桥接模式很适合,这样他们都能在自己的层级扩展
public interface Account {
Account openAccount();
void showAccountType();
}
//定期账号
public class DepositAccount implements Account{
@Override
public Account openAccount() {
System.out.println("打开定期账号");
return new DepositAccount();
}
@Override
public void showAccountType() {
System.out.println("这是一个定期账号");
}
}
//活期账号(随时取钱)
public class SavingAccount implements Account {
@Override
public Account openAccount() {
System.out.println("打开活期账号");
return new SavingAccount();
}
@Override
public void showAccountType() {
System.out.println("这是一个活期账号");
}
}
抽象端:
//银行类携程抽象类,因为现在要把Account引入到Bank里边,通过组合的方式交给子类来实现它的行为
public abstract class Bank {
//注意修饰符,只有子类能拿到Account
protected Account account;
public Bank(Account account) {
this.account = account;
}
//写抽象方法正是Account接口的抽象方法(不一定声明成一样的方法名),要委托给Account
//Bank是抽象类,这个抽象类的某个行为委托给Account接口(实现就是Account的两个实现类)
abstract Account openAccount();
}
public class ABCBank extends Bank {
//当父类中只有有参构造器时,默认在子类中需要使用super才能调用父类的有参构造器,可以实现继承。
public ABCBank(Account account) {
super(account);
}
@Override
Account openAccount() {
System.out.println("打开中国农业银行账号");
account.openAccount(); //委托给Account来实现,通过委托,无论这个方法怎么变,这边都不用改
return account; //直接用父类的Account
}
}
public class ICBCBank extends Bank {
public ICBCBank(Account account) {
super(account);
}
@Override
Account openAccount() {
System.out.println("打开工商银行账号");
account.openAccount(); //委托给Account来实现
return account; //直接用父类的Account
}
}
测试类:
public class Test {
public static void main(String[] args) {
ICBCBank icbcBank = new ICBCBank(new DepositAccount());
Account icbcAccount = icbcBank.openAccount(); //打开工商银行账号
icbcAccount.showAccountType(); //这是一个定期账号
ABCBank abcBank = new ABCBank(new SavingAccount());
Account abcAccount = abcBank.openAccount(); //打开中国农业银行账号
abcAccount.showAccountType(); //这是一个活期账号
}
}
三、源码解析
JDBC的Driver接口,mysql的Driver、Oracle的Driver都是Driver的具体实现部分。