一 定义
桥接模式也成为桥梁模式,是结构型设计模式之一。现实生活中,桥梁具有连接两岸的作用,具有承接两边的作用。在程序中,两边具体指哪两边呢,接下来看定义。
定义:将抽象部分与实现部分分离,使它们可以独立地进行变化。
从桥接模式的定义,我们可以看出,桥接模式连接的两边,分别是抽象部分和实现部分,使它们独立变化,起到解耦的作用。
二 模式结构
角色介绍
Abstraction:抽象部分
该类保持一个对实现部分对象的引用,该类中的方法需要调用实现部分的对象来实现。RefinedAbstraction:优化的抽象部分
抽象部分的具体实现,该类一般对抽象部分的方法进行完善和拓展。Implementor:实现部分
可以使接口或者抽象类,定义角色必须的行为和属性。ConcreteImplementorA/ConcreteImplementorB :实现部分的具体实现
完善实现部分中方法定义的具体逻辑。Client:客户端
实现部分的接口
/*
* 实现部分的抽象接口
* @author Jackson
* @version 1.0.0
* since 2019 03 05
*/
public interface Implementor {
/**
* 实现抽象部分的具体方法
*/
public void operationImpl();
}
- 实现部分的具体实现一
/*
* 实现部分的具体实现一
* @author Jackson
* @version 1.0.0
* since 2019 03 05
*/
public class ConcreteImplementorA implements Implementor{
@Override
public void operationImpl() {
// 具体的实现
}
}
- 实现部分的具体实现二
/*
* 实现部分的具体实现二
* @author Jackson
* @version 1.0.0
* since 2019 03 05
*/
public class ConcreteImplementorB implements Implementor{
@Override
public void operationImpl() {
// 具体的实现
}
}
- 抽象部分的实现
public abstract class Abstraction {
private Implementor mImplementor; // 声明一个成员变量引用实现部分的对象
/**
* 构造方法
* @param implementor
*/
public Abstraction(Implementor implementor){
this.mImplementor=implementor;
}
/**
*通过调用实现部分的具体方法实现具体的功能
*/
public void operation(){
mImplementor.operationImpl();
}
}
- 抽象部分的完善
public class RefinedAbstraction extends Abstraction{
/**
* 构造方法
*
* @param implementor
*/
public RefinedAbstraction(Implementor implementor) {
super(implementor);
}
/**
* 对父类抽象部分的方法进行扩展
*/
public void refinedOperation(){
}
}
三 实例
我们以生活中特别贴切的买奶茶为例,我们去奶茶店买奶茶,一般分为四种:大杯加糖、大杯不加糖、小杯加糖、小杯不加糖,所以对于一杯奶茶来说,这四种变化实质上就只有两种变化,一是大小杯,而是加糖不加糖。两种维度可以独立变化。
这里有两个维度,我们选取往奶茶里添不添东西作为实现部分,把大小杯作为抽象部分,反过来也是可以的,模式中定义的“抽象”与“实现”,本质上是两个独立变化的维度。
- 实现部分
/*
* 实现部分
* @author Jackson
* @version 1.0.0
* since 2019 03 05
*/
public abstract class MilkTeaAddtives {
/**
* 具体往奶茶里添加什么,由子类实现
* @return
*/
public abstract String addSomething();
}
- 实现部分的具体实现一,加糖
/*
* 实现部分的具体实现,加糖
* @author Jackson
* @version 1.0.0
* since 2019 03 06
*/
public class SugerAddMilkTea extends MilkTeaAddtives{
@Override
public String addSomething() {
return "加糖";
}
}
- 实现部分的具体实现二,原味
/*
* 实现部分的具体实现二 ,普通
* @author Jackson
* @version 1.0.0
* since 2019 03 06
*/
public class OrdinaryMilkTea extends MilkTeaAddtives{
@Override
public String addSomething() {
return "原味";
}
}
- 抽象部分,要有一个对实现部分对象的引用,具体做什么由其子类完善
/*
* 抽象部分
* @author Jackson
* @version 1.0.0
* since 2019 03 05
*/
public abstract class MilkTea {
protected MilkTeaAddtives mMilkTeaAddtives; // 声明一个实现部分对象的引用
/**
* 构造方法
* @param milkTeaAddtives
*/
public MilkTea(MilkTeaAddtives milkTeaAddtives) {
this.mMilkTeaAddtives = milkTeaAddtives;
}
/**
* 具体什么样的奶茶由子类决定
*/
public abstract void makeMilkTea();
}
- 抽象部分的完善一,大杯
public class LargeMilkTea extends MilkTea{
/**
* 构造方法
*
* @param milkTeaAddtives
*/
public LargeMilkTea(MilkTeaAddtives milkTeaAddtives) {
super(milkTeaAddtives);
}
@Override
public void makeMilkTea() {
System.out.println("大杯的"+mMilkTeaAddtives.addSomething()+"奶茶");
}
}
- 抽象部分的完善二 ,小杯
public class SmallMilkTea extends MilkTea{
/**
* 构造方法
*
* @param milkTeaAddtives
*/
public SmallMilkTea(MilkTeaAddtives milkTeaAddtives) {
super(milkTeaAddtives);
}
@Override
public void makeMilkTea() {
System.out.println("小杯的"+mMilkTeaAddtives.addSomething()+"奶茶");
}
}
- 测试代码
// 原味的
OrdinaryMilkTea ordinaryMilkTea=new OrdinaryMilkTea();
// 加糖
SugerAddMilkTea sugerAddMilkTea=new SugerAddMilkTea();
// 大杯原味
LargeMilkTea largeMilkTeaOrdinary=new LargeMilkTea(ordinaryMilkTea);
largeMilkTeaOrdinary.makeMilkTea();
// 大杯加糖
LargeMilkTea largeMilkTeaSuger=new LargeMilkTea(sugerAddMilkTea);
largeMilkTeaSuger.makeMilkTea();
// 小杯原味
SmallMilkTea smallMilkTeaOrdinary=new SmallMilkTea(ordinaryMilkTea);
smallMilkTeaOrdinary.makeMilkTea();
// 小杯加糖
SmallMilkTea smallMilkTeaSuger=new SmallMilkTea(sugerAddMilkTea);
smallMilkTeaSuger.makeMilkTea();
-
运行结果
桥接模式具有良好的扩展性,因为桥接模式的两个维度独立变化的,比如,如果我们现在想扩展一下,新增中杯的奶茶,只需新增一种抽象部分的实现模式即可。
- 中杯奶茶的抽象部分的完善
public class MiddleMilkTea extends MilkTea{
/**
* 构造方法
*
* @param milkTeaAddtives
*/
public MiddleMilkTea(MilkTeaAddtives milkTeaAddtives) {
super(milkTeaAddtives);
}
@Override
public void makeMilkTea() {
System.out.println("中杯的"+mMilkTeaAddtives.addSomething()+"奶茶");
}
}
- 测试代码
//中杯原味
MiddleMilkTea middleMilkTeaOrdinary=new MiddleMilkTea(ordinaryMilkTea);
middleMilkTeaOrdinary.makeMilkTea();
// 中杯加糖
MiddleMilkTea middleMilkTeaSuger=new MiddleMilkTea(sugerAddMilkTea);
middleMilkTeaSuger.makeMilkTea();
运行结果就不在这里展示了。
同样,如果要增加口味,也可以让实现部分变化起来,比如,加珍珠,加蓝莓等等,这里就不一一举例了。
可以看出,不管是口味(MilkTeaAddtives)的变化,还是大小杯(MilkTea)的变化,都是独立的,没有交集,两者唯一的联系是MilkTea中保持对MilkTeaAddtives的引用,此为两者的纽带,这就是桥接模式。
四 优点
桥接模式主要有以下几个优点
- 抽象和实现分离,这也是桥接模式的主要特点,该模式让抽象部分和实现部分独立开来,分别定义接口,极大地提高了系统的灵活性。
- 更好的扩展性
由于桥接模式把抽象和现实分离开了,这就使得抽象部分和实现部分可以独立地扩展而不会相互影响。 - 可减少子类的个数,对于有两个维度的变化,如果采用继承的方式,子类的数量大约是两个维度乘积,采用桥接模式可以大幅度减少子类的个数。
五 使用场景
- 如果不希望抽象部分和实现部分采用固定的绑定关系,可以使用桥接模式。
- 如果出现抽象部分和实现部分都能扩展的情况,可以采用桥接模式,让抽象部分和实现部分独立地变化。
- 如果采用了继承会导致产生很多子类,可以考虑采用桥接模式,分析变化的原因,看看能否分离出不同的维度,然后通过桥接模式连接。