桥接模式,也叫作桥梁模式,英文是 Bridge Design Pattern。
在 GoF 的《设计模式》一书中,桥接模式是这么定义的:“Decouple an abstraction from its implementation so that the two can vary independently。”翻译成中文就是:“将抽象和实现解耦,让它们可以独立变化。”
定义中“抽象”和“实现”两个概念,是理解桥接模式的关键。定义中的“抽象”,指的并非“抽象类”或“接口”,而是被抽象出来的一套“类库”,它只包含骨架代码,真正的业务逻辑需要委派给定义中的“实现”来完成。而定义中的“实现”,也并非“接口的实现类”,而是的一套独立的“类库”。“抽象”和“实现”独立开发,通过对象之间的组合关系,组装在一起。
很多书籍、资料中,还有另外一种理解方式:“一个类存在两个(或多个)独立变化的维度,我们通过组合的方式,让这两个(或多个)维度可以独立进行扩展。”通过组合关系来替代继承关系,避免继承层次的指数级爆炸。这种理解方式非常类似于“组合优于继承”设计原则。
适用场景
如果一个系统需要在构建的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,可以通过桥接模式使他们在抽象层建立一个关联关系;
那些不希望使用继承或因为多层次继承导致系统类的个数极具增加的系统;
一个类存在两个独立变化的维度,而这两个维度都需要进行扩展。
UML 类图
通过类图发现,桥接模式分为以下四个角色:
Abstract: 定义抽象接口,拥有一个Implementor类型的对象引用
RefinedAbstraction: 扩展Abstraction中的接口定义
Implementor: 实现部分,可以为接口或者是抽象类,其方法不一定要与抽象部分中的一致,一般情况下是由实现部分提供基本的操作,而抽象部分定义的则是基于实现部分基本操作的业务方法
ConcreteImplementorA 和 ConcreteImplementorB: 实现Implementor接口,给出具体实现
通用代码:
首先是实现部分代码:
Implementor.java
public interface Implementor {
void operationImpl();
}
ConcreteImplementorA.java
public class ConcreteImplementorA implements Implementor{
@Override
public void operationImpl() {
// 具体实现
}
}
ConcreteImplementorB.java
public class ConcreteImplementorB implements Implementor {
@Override
public void operationImpl() {
// 具体实现
}
}
然后是抽象部分的代码:
Abstraction.java
public abstract class Abstraction {
private Implementor implementor;
public Abstraction(Implementor implementor) {
this.implementor = implementor;
}
public void operation() {
implementor.operationImpl();
}
}
RefinedAbstraction.java
public class RefinedAbstraction extends Abstraction{
public RefinedAbstraction(Implementor implementor) {
super(implementor);
}
@Override
public void operation() {
// 对 Abstraction 中的 operation() 方法进行扩展
}
}
使用
public class Test {
public static void main(String[] args) {
Implementor concreteImplementorA = new ConcreteImplementorA();
Abstraction refinedAbstraction = new RefinedAbstraction(concreteImplementorA);
refinedAbstraction.operation();
}
}
示例
场景: 一个甜品店,售卖的食品有蛋糕、奶茶和牛奶,但是每个商品都有各种口味:草莓味、芒果味、香蕉味。
1、创建食品类,该类为抽象类,主要提供添加add的方法;
public abstract class Food {
//水果
Fruit fruit;
public void setFruit(Fruit fruit) {
this.fruit = fruit;
}
/**
* 制作时添加
*/
public abstract void add();
}
食品类的子类一:蛋糕
public class Cake extends Food {
@Override
public void add() {
fruit.beAdd("蛋糕");
}
}
食品类的子类二:牛奶
public class Milk extends Food {
@Override
public void add() {
fruit.beAdd("牛奶");
}
}
食品类的子类三:奶茶
public class MilkTea extends Food {
@Override
public void add() {
fruit.beAdd("奶茶");
}
}
创建水果接口类
public interface Fruit {
/**
* 被添加
* @param food
*/
public void beAdd(String food);
}
水果接口实现类一:香蕉
public class Banana implements Fruit {
@Override
public void beAdd(String food) {
System.out.println("香蕉" + food);
}
}
水果接口实现类二:草莓
public class Strawberry implements Fruit {
@Override
public void beAdd(String food) {
System.out.println("草莓" + food);
}
}
水果接口实现类三:芒果
public class Mango implements Fruit {
@Override
public void beAdd(String food) {
System.out.println("芒果" + food);
}
}
主方法测试
public class TestMain {
public static void main(String[] args) {
//蛋糕
Fruit mango = new Mango();
Food cake = new Cake();
cake.setFruit(mango);
cake.add();
//牛奶
Food milk = new Milk();
milk.setFruit(mango);
milk.add();
}
}
输出结果:
芒果蛋糕
芒果牛奶
桥接模式优缺点
1、优点:
桥接模式可以取代多层继承的方案。 多层继承违背了单一职责原则,复用性较差,类的个数也非常多。桥接模式可以极大的减少子类的个数,从而降低管理和维护的成本。
桥接模式极大的提高了系统可扩展性,在两个变化维度中任意扩展一个维度,都不需要修改原有的系统,符合开闭原则。
2、缺点
会增加系统的理解与设计难度,由于关联关系建立在抽象层,要求开发者一开始就针对抽象层进行设计与编程
正确识别出系统中两个独立变化的维度并不是一件容易的事情