什么是桥接模式?
桥接模式是一种对象结构型模式。
桥接模式将抽象层和实现层进行分离,是它们可以独立的变化,又称为柄体(Handler and Body)模式和接口(Interface)模式。
其实桥接模式很好理解,比如说知名键盘厂商Filco需要生产104键/双模、87键/双模的键盘,同时还需要满足基本的四大轴青、茶、红、黑,按照以往的设计流程,我们可能会使用工厂模式,创建不同的键位键盘生产方法,在每种键位键盘的生产方法中再根据传入参数的不同,组装不同的轴体。但是这种业务实现有一个弊端,就是如果厂商需要添加新的轴体,或者添加新的键盘布局方案,势必要对同一个类进行多次、大量的修改,在一定程度上完成代码设计的同时,也违背了单一职责原则和开闭原则。我们可以换一个思路,将键位布局和键盘轴体分离开来,形成两个独立变化维度,从而将它们设计成两个独立的继承等级结构,并为两个维度都提供抽象层,建立抽象耦合。通常情况下,我们将具有两个独立变化维度的类的一些普通业务实现和与之关系最密切的维度设计为抽象类层次结构,而将另一个维度设计为实现类层次结构,也就是说,我们可以认为,键盘布局是键盘的抽象部分,而键盘轴体是键盘的实现部分。
核心结构
- Abstraction。抽象类,主要负责定义抽象层,并定义一些具体业务方法或者是抽象业务方法,通常是抽象类,它还维护一个Implementor对象,用于和Implement进行关联。
- RefinedAbstraction。扩充抽象类,主要负责扩充Abstraction定义的抽象层,通常情况下,它不是抽象类而是具体实现类,它实现了Abstraction声明的抽象业务方法,并可以调用Implementor对象中声明的业务方法。
- Implementor。实现类接口,定义实现类接口,Implementor可以和Abstraction完全不同。一般情况下,Implementor疾厄宫提供基本操作,而Abstraction接口可能会提供更多复杂的业务实现。Implementor声明一些基本的方法,而具体的实现则交给具体实现类(ConcreteImplementor)去实现,通过关联关系,Abstraction不仅拥有自己定义的方法,而且还可以调用Implementor中定义的方法。
- ConcreteImplementor。具体实现类,主要负责Implementor接口中方法的具体业务实现,不同的ConcreteImplementor提供不同的具体业务实现,为Abstraction的调用提供更多的可能性。
类图展示
代码分析
在推送系统中,推送方式和推送人群中是两种不同的维度,在抽象工厂模式中,我们将推送人群设计为产品,将推送方式设计为生产工厂,但是在新增或修改推送人群时,可能会对推送方式进行大量的代码修改。我们还是举这个例子,利用桥接模式对代码进行一些修改和优化。
首先,我们需要定义实现类接口——推送人群:
/**
* Created by <sunshine> mysunshinedreams@163.com on 2017/1/17 0017.
* 桥接模式——实现类接口
*/
public interface Group {
public String group();
}
我们根据实现类接口进行推送人群具体分类。
青少年人群:
/**
* Created by <sunshine> mysunshinedreams@163.com on 2017/1/17 0017.
* 桥接模式——具体接口类
*/
public class TeenageGroup implements Group {
@Override
public String group() {
return "teenage group";
}
}
成年人群:
/**
* Created by <sunshine> mysunshinedreams@163.com on 2017/1/17 0017.
* 桥接模式——具体接口类
*/
public class AdultGroup implements Group {
@Override
public String group() {
return "adult group";
}
}
年长人群:
/**
* Created by <sunshine> mysunshinedreams@163.com on 2017/1/17 0017.
* 桥接模式——具体接口类
*/
public class ElderGroup implements Group {
@Override
public String group() {
return "elder group";
}
}
实现类这面已经搞定,接着我们就应该实现抽象类,首先需要定义一个抽象类——推送方式:
/**
* Created by <sunshine> mysunshinedreams@163.com on 2017/1/17 0017.
* 桥接模式——抽象类
*/
public abstract class Push {
protected Group group;
public void setGroup(Group group) {
this.group = group;
}
public abstract void push();
}
根据这个抽象类,我们需要需求实现自己的业务扩充。
全量推送方式:
/**
* Created by <sunshine> mysunshinedreams@163.com on 2017/1/17 0017.
* 桥接模式——扩展抽象类
*/
public class FullAmountPush extends Push {
@Override
public void push() {
System.out.println("full amount push to the group which the name is ".concat(group.group()));
}
}
指定推送方式:
/**
* Created by <sunshine> mysunshinedreams@163.com 2017/1/17 0017.
* 桥接模式——扩展抽象类
*/
public class AppointPush extends Push {
@Override
public void push() {
System.out.println("appoint push to the group which the name is ".concat(group.group()));
}
}
地域推送方式:
/**
* Created by <sunshine> mysunshinedreams@163.com on 2017/1/17 0017.
* 桥接模式——扩展抽象类
*/
public class DistrictPush extends Push {
@Override
public void push() {
System.out.println("district push to the group which the name is ".concat(group.group()));
}
}
模式总结
优点
- 桥接模式使用对象之间的组合关系,对抽象和具体实现之间的继承关系进行解耦,使得抽象和实现可以沿着各自的维度进行变化,也就是说,抽象和具体实现不在同一个继承层次结构中,而是子类化他们,抽象和实现都有自己的子类,然后对子类进行组装,从而获得更多维度的组合对象。
- 符合开闭原则,任意扩展其中一个变化维度,都不需要修改原有系统,提高了系统的扩展性。
- 符合单一继承原则,多层继承方案复用性较差,并且违背了单一继承原则,每次扩展,都回引起类的数量增多,而桥接模式是比多继承方式更高效的方案,它极大的减少了子类的个数。
缺点
- 桥接模式要求在设计时识别出系统中存在的两个维度,使用范围具有一定的局限性,同时对设计者的经验具有一定的挑战。
- 增加系统的设计难度和复杂度,由于关联关系建立在抽象层,所以要求设计者在设计之初就应该明确如何对系统进行设计。
适用场景
- 系统中抽象层和实现层需要更多的灵活性,通过桥接模式可以在抽象层和实现层建立组合关联挂席,避免在两个层次之间建立静态关系。
- 系统中有至少两个独立变化的维度,并且维度都需要独立进行扩展。
- 实现层的改变不会影响外部调用,也就是说,实现层对外部调用是透明的。
- 不希望使用多继承结构,或者是饱受多继承带来的类极速扩张的情形下,桥接模式尤为适用。