抽象类像一座桥一样,把接口实现和抽象类的实现连接起来。
1.桥接模式
桥接模式是一种结构型设计模式,它通过将抽象部分与实现部分分离来解耦。它使用抽象类作为桥梁,将接口实现与抽象类的实现的代码独立开来,从而使它们可以各自独立地变化。桥接模式的核心思想是“组合优于继承”。
简单来讲,就是一个类的功能有两个维度来控制,两个维度都有不同的实现,可以进行随意的组合。
为了达到让抽象部分和实现部分独立变化的目的,抽象部分会拥有实现部分的接口对象,有了实现部分的接口对象之后,就能够通过这个接口来调用具体实现部分的功能。桥接在程序上就体现成了抽象部分拥有实现部分的接口对象,实现了一种桥接关系。桥接模式中的桥接是一个单方向的关系,只能够抽象部分去使用实现部分的对象。
抽象化角色Abstraction:抽象化给出的定义,其中保存了一个对实现化对象的引用。
修正抽象化角色Refined Abstraction:扩展抽象化角色,改变和修正父类对抽象化的定义。
实现化角色Implementor:给出实现化角色的接口,但不给出具体的实现。注意,这个接口不一定和抽象化角色的接口定义相同,实际上,这两个接口可以非常不一样。实现化角色应当只给出底层操作,而抽象化角色应当只给出基于底层操作的更高一层的操作。
具体实现化角色ConcreteImplementor:给出实现化角色接口的具体实现。
抽象部分:
public abstract class Abstraction {
private Implementor implementor; //在抽象部分会拥有实现部分的接口对象,接口作为桥梁,使得抽象实现类的功能独立于接口实现类
public Abstraction(Implementor implementor
this.implementor = implementor;
}
public void operation() {
implementor.operationImpl();
}
}
public class RefinedAbstraction extends Abstraction {
public RefinedAbstraction(Implementor implementor) {
super(implementor);
}
public void refinedOperation() {
//对 Abstraction 中的 operation 方法进行扩展
}
}
实现部分:
public interface Implementor {
void operationImpl();
}
public class ConcreteImplementorA implements Implementor{
@Override
public void operationImpl() {
//具体实现
}
}
public class ConcreteImplementorB implements Implementor{
@Override
public void operationImpl() {
//具体实现
}
}
通过结构可以清楚地看到抽象类Abstraction就像一个桥梁一样将RefinedAbstraction和Implementor连接起来。
2.桥接模式的使用场景
①如果一个系统需要在构建的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。
②抽象化角色和实现化角色可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将一个抽象化子类的对象和一个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合。
③一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。
④虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者。
⑤对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。
3.桥接模式的优缺点
1)优点:
①抽象部分和实现部分分离
桥接模式分离了抽象部分和实现部分,从而极大的提供了系统的灵活性,让抽象部分和实现部分独立开来,分别定义接口,这有助于系统进行分层设计,从而产生更好的结构化系统。对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了。
②更好的可扩展性
桥接模式分别定义抽象部分和实现部分的接口,这就使得抽象部分和实现部分可以分别独立扩展,而不会相互影响,大大提高了系统的可扩展性。
③可动态的切换实现
由于桥接模式实现了抽象和实现的分离,所以在实现桥接模式时,就可以实现动态的选择和使用具体的实现。
④实现细节对客户端透明,可以对用户隐藏实现细节。
2)缺点:
①桥接模式的引入增加了系统的理解和设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计和编程。
②桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围有一定的局限性。
4.桥接模式示例
为了更好地理解桥接模式,来看一个简单的示例:假设有一个形状类,其中有一个颜色属性。此时,如果使用继承来实现不同颜色的形状类,就需要创建许多子类,使代码结构变得复杂且难以维护。而使用桥接模式,则可以将形状和颜色分别抽象出来,从而将它们独立开来。
public interface Color {
String getColor();
}
public class Red implements Color {
@Override
public String getColor() {
return "红色";
}
}
public class Blue implements Color {
@Override
public String getColor() {
return "蓝色";
}
}
public abstract class Shape {
protected Color color;
public Shape(Color color) {
this.color = color;
}
public abstract void draw();
}
public class Rectangle extends Shape {
public Rectangle(Color color) {
super(color);
}
@Override
public void draw() {
System.out.println("绘制一个" + color.getColor() + "的矩形");
}
}
public class Circle extends Shape {
public Circle(Color color) {
super(color);
}
@Override
public void draw() {
System.out.println("绘制一个" + color.getColor() + "的圆形");
}
}
在上面的代码中,首先定义了一个颜色接口,并分别实现了红色和蓝色。然后,定义了一个抽象的形状类,其中有一个颜色属性和一个抽象方法draw()。最后,实现了具体的矩形和圆形类,并在构造函数中传入了一个颜色对象。
这样,就将形状和颜色分别抽象出来,从而实现了解耦。当需要创建不同颜色的形状时,只需要创建不同颜色的对象并传入即可。
5.Android源码中的使用
Window与WindowManager之间使用的就是桥接模式。
其中Window和PhoneWindow构成窗口的抽象部分,Window为抽象部分的接口,PhoneWindow为抽象部分的具体实现以及扩展,而WindowManager则为实现部分的基类,WindowManagerImpl为实现部分具体的逻辑实现,它使用WindowManagerGlobal通过IWindowManager接口与WMS进行通信,最终由WMS完成具体窗口的管理工作。是典型的桥接设计模式。