桥接模式属于结构型设计模式。
设计意图:将抽象部分与实现部分分离,使它们都可以独立的变化。
一看到设计意图,大家可能有些发懵,我们看到的继承和接口不都是抽象和实现分离的吗?尤其是接口和抽象类都是这样的实现啊!那怎么还有这么个桥接的分离呢?
我们先来看个例子。
例如:汽车品牌内置导航仪,我们希望实现,每个品牌的导航仪都可以在任何一个牌子的汽车上安装并启动。
汽车品牌有两个:宝马、奔驰。
导航仪有三个牌子:神行者、北斗、高德。
来看正常的设计,我们肯定是会这样的采用继承来实现每个组合的安装和启动处理的,每个品牌+导航都需要一个独立的类来实现功能,但是这里有个一个问题,如果我们再增加一个品牌那么就意味着要再增加三个类,每增加一个品牌都是如此,这是一件太痛苦的事啊!所以能找到一种方式不用增加类就可以实现新品牌安装并开启导航仪的功能吗?
来看下面的图,我们将汽车品牌和导航仪品牌做一个组合。
如果能这样组合,那每增加一个汽车品牌时,就只需要增加一个品牌类就好,这个品牌和之前的导航仪组合就可以了。
我们可以看出导航仪和汽车其实是一种聚合关系,也就是导航仪只是汽车的一部分,而这一部分不随着汽车消亡而消失。所以他们之间是松耦合关系,聚合关系。
桥接模式是遵循了合成/聚合复用原则(稍后我们在介绍该原则),我们来看通用通用类图。
Abstraction:定义抽象类的接口,该接口中定义如何使用Implementor接口类型对象的方法。
RefinedAbstraction:实现由Abstraction定义的接口方法,可能会有自己的一些私有方法。
Implementor:定义Abstraction抽象的实现,这种实现是一种嫁接的关系,就像计算机主板和声卡、显卡的关系一样,它可以是一个接口, Implementor接口提供Abstraction抽象接口需要的操作,而 Abstraction则定义了基于这些基本操作的较高层次的操作。
ConcreteImplementor:实现Implementor接口并定义它的具体实现。
让我们来看实例代码,通过实例代码可以更好的理解桥接模式的神奇,它真的是实现可插拔式的模式。
package com.bridge;
/**
* 1:汽车品牌抽象类,这个抽象类主要是导航的安装与启动,其它的配件也可以在该类提供方法,
* 但是为了开-闭原则,最好可以在另加其它接口。
* 2:桥接就是要实现插拔方式的调用,我们想一下主板和插件,主板提供接口定义该接口的功能,
* 由插件来实现该接口的功能。这就是桥接的主要思想,抽象与实现分离。
* 3:该类定义了如何注入一个接口的方法install,并定义了如何操作注入接口的具体实现的方法open。
* 4:这里其实可以使用接口,如果使用接口,那就更灵活,以后我再给大家写一个多个接口的例子
* ,每个配件一个接口。这样每个品牌的汽车可以实现多个接口也就可以增加不同的配件了。
* @author gaoxu
* 实践出真知!
*/
public abstract class AbstractVehicleBrand {
/**安装导航方法
* @author gaoxu
* @param n
*/
public abstract void install(INavigator n);
/**开启导航
* 开启导航是调用导航接口实现类的方法,其实那个方法就是我们这个方法的延伸。
* @author gaoxu
*/
public abstract void open();
}
package com.bridge;
/**
* @author gaoxu
* 实践出真知!
*/
public class BMWVehicle extends AbstractVehicleBrand{
//导航
INavigator navigator = null;
@Override
public void install(INavigator n) {
navigator = n;
}
@Override
public void open() {
navigator.work();
}
}
package com.bridge;
/**
* @author gaoxu
* 实践出真知!
*/
public class BenzVehicle extends AbstractVehicleBrand{
//导航
INavigator navigator = null;
@Override
public void install(INavigator n) {
navigator = n;
}
@Override
public void open() {
navigator.work();
}
}
导航插件的公共接口,定义了导航的行为方法。package com.bridge;
/**
* @author gaoxu
* 实践出真知!
*/
public interface INavigator {
public void work();
}
package com.bridge;
public class SXZNavigator implements INavigator{
@Override
public void work() {
System.out.println("我是神行者导航!");
}
}
package com.bridge;
public class BDNavigator implements INavigator{
@Override
public void work() {
System.out.println("我是北斗导航!");
}
}
我们来看客户端代码
package com.bridge;
public class Client {
public static void main(String[] para){
//我们现在是实现不同品牌的汽车,可以安装不同牌子的导航,也就是把汽车和导航聚合了起来。
//我们是通过桥接的方式完成了这种聚合,桥接方式比继承的方式要更灵活,它是汽车与配件可
以独立各自的发展。
//我们可以实现的聚合关系:宝马+北斗,宝马+神行者,奔驰+北斗,奔驰+身形者
//当然我们还可以给汽车配置更多不同的后装配件例如:空气净化器等。
INavigator bdNavigator = new BDNavigator();
INavigator sxzNavigator = new SXZNavigator();
//宝马安装北斗导航
AbstractVehicleBrand bmw = new BMWVehicle();
bmw.install(bdNavigator);
bmw.open();
//奔驰安装了神行者导航
AbstractVehicleBrand benz = new BMWVehicle();
benz.install(sxzNavigator);
benz.open();
}
}
使用范围:多个具有变化的分类共同实现功能时易采用桥接模式。