版权申明】非商业目的可自由转载
博文地址:https://blog.csdn.net/ShuSheng0007/article/details/88370067
出自:shusheng007
设计模式系列文章:
秒懂Java代理与动态代理模式
秒懂设计模式之建造者模式(Builder Pattern)
秒懂设计模式之工厂方法模式(Factory Method Pattern)
秒懂设计模式之抽象工厂模式(Abstract Factory Pattern)
秒懂设计模式之策略模式(Strategy Pattern)
人在IT江湖飘,不懂设计模式咋装X?
桥接模式在日常开发中不是特别常用,主要是因为上手难度较大,但是对于理解面向对象设计有非常大的帮助。
桥接模式是将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interfce)模式。
桥我们大家都熟悉,顾名思义就是用来将河的两岸联系起来的。而此处的桥是用来将两个独立的结构联系起来,而这两个被联系起来的结构可以独立的变化,所有其他的理解只要建立在这个层面上就会比较容易。
下面是一些官方的说明,比较晦涩,必须等你有一定的经验后才能理解:
如果一个系统需要在抽象化和具体化之间增加更多的灵活性,避免在两个层次之间建立静态的继承关系,通过桥接模式可以使它们在抽象层建立一个关联关系。
“抽象部分”和“实现部分”可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将一个抽象化子类的对象和一个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合。
一个类存在两个(或多个)独立变化的维度,且这两个(或多个)维度都需要独立进行扩展。
对于那些不希望使用继承或因为多层继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。
在讲策略模式的时候,王二狗和牛翠花不是要到天津之眼去约炮,不,约会嘛,两人到那后先去星巴克喝咖啡了,星巴克提供了多种选择,从容量上说有大杯,中杯,小杯,从口味上说有原味,加糖。这可难为了有选择恐惧症的牛翠花,半天点不出来,后面的人都开始骂娘了,王二狗也只能陪着笑脸道歉。
其实被难为的除了牛翠花,还有给星巴克做订单系统的外包公司的程序员林蛋大。星巴克一开始提需求的时候只有正常杯(中杯),原味和加糖这几种选择,人家林蛋大也是有两年工作经验的码农,这需求不在话下,蛋大还想到了要面对抽象编程。
首先定义一个点咖啡接口,里面有一个下单方法,至于点哪种口味的咖啡,就由其子类去决定,完美,蛋大好牛逼啊!
public interface ICoffee {
void orderCoffee(int count);
}
原味咖啡类
public class CoffeeOriginal implements ICoffee {
@Override
public void orderCoffee(int count) {
System.out.println(String.format("原味咖啡%d杯",count));
}
}
加糖咖啡类
public class CoffeeWithSugar implements ICoffee {
@Override
public void orderCoffee(int count) {
System.out.println(String.format("加糖咖啡%d杯",count));
}
}
搞定收工,王者荣耀搞起!项目经理突然过来了:蛋大啊,客户那边说了,他们准备增加两个容量规格的咖啡,大杯和小杯,你改一下。林蛋大:what?尼玛为什么不早说,老子刚写完。胸中虽有万千牢骚,还得平复一下心情去改代码,何必呢?蛋大心中暗喜,幸好老子是面向抽象编程的,对应加几个实现类不就得了?
//中杯加糖
public class CoffeeWithSugar implements ICoffee {
@Override
public void orderCoffee(int count) {
System.out.println(String.format("中杯加糖咖啡%d杯",count));
}
}
//大杯加糖
public class LargeCoffeeWithSugar implements ICoffee {
@Override
public void orderCoffee(int count) {
System.out.println(String.format("大杯加糖咖啡%d杯",count));
}
}
//小杯加糖
public class SamllCoffeeWithSugar implements ICoffee {
@Override
public void orderCoffee(int count) {
System.out.println(String.format("小杯加糖咖啡%d杯",count));
}
}
...
加着加着蛋大慌了,共需要3x2=6个类啊,大杯原味和加糖,中杯原味和加糖,小杯原味和加糖。万一过段时间那二笔客户又要出加奶,加蜂蜜等等口味,说不定还有迷你杯,女神杯等等规格的咖啡,那我这边的类不就爆炸了吗?看来的去找个设计模式了。。。
此场景桥接模式正合适,这里有两个变化维度,咖啡的容量和口味,而且都需要独立变化。如果使用继承的方式,随着变化类就会急剧的增加。你可以将容量理解为抽象部分,而口味理解为实现部分,这两个部分需要桥接。
抽象化角色就像是一个水杯的手柄,而实现化角色和具体实现化角色就像是水杯的杯身。手柄控制杯身,这就是此模式别名“柄体”的来源。
林蛋大查了半天不知道使用哪个设计模式,只能求助前辈王二狗了。电话通了:狗哥,我这有个问题。。。王二狗:我这正解决终身大事呢,没工夫理你,你去调查一下桥接模式。林蛋大:不愧是大神,改天请你和嫂子吃饭,先挂啦。挂电话后,蛋大就去查找相关桥接模式的资料去了。
蛋大分析了当前业务场景,认为可以将咖啡的容量作为抽象化Abstraction,而咖啡口味为实现化Implementor
第一步:创建实现化部分的接口定义(咖啡口味的维度)
public interface ICoffeeAdditives {
void addSomething();
}
第二步:创建抽象化部分的接口定义(咖啡容量的维度)
//抽象化Abstraction
public abstract class Coffee {
protected ICoffeeAdditives additives;
public Coffee(ICoffeeAdditives additives){
this.additives=additives;
}
public abstract void orderCoffee(int count);
}
我们可以看到,Coffee
持有了ICoffeeAdditives
引用,ICoffeeAdditives
的实例是通过构造函数注入的,这个过程就是我们所说的桥接过程。我们通过这个引用就可以调用ICoffeeAdditives
的方法,进而将Coffee
的行为与ICoffeeAdditives
的行为通过orderCoffee()
方法而组合起来。
下面是一个对抽象化修正的一个类,蛋大在里面增加了一个用于品控的方法
//抽象化的修正类
public abstract class RefinedCoffee extends Coffee {
public RefinedCoffee(ICoffeeAdditives additives) {
super(additives);
}
public void checkQuality(){
Random ran=new Random();
System.out.println(String.format("%s 添加%s",additives.getClass().getSimpleName(),ran.nextBoolean()?"太多":"正常"));
}
}
第三步:实现实现化部分(咖啡口味维度)的接口ICoffeeAdditives
//加奶
public class Milk implements ICoffeeAdditives {
@Override
public void addSomething() {
System.out.println("加奶");
}
}
//加糖
public class Sugar implements ICoffeeAdditives {
@Override
public void addSomething() {
System.out.println("加糖");
}
}
第四步:实现抽象化部分(咖啡的容量维度)的接口Coffee
//大杯
public class LargeCoffee extends RefinedCoffee {
public LargeCoffee(ICoffeeAdditives additives) {
super(additives);
}
@Override
public void orderCoffee(int count) {
additives.addSomething();
System.out.println(String.format("大杯咖啡%d杯",count));
}
}
//小杯
public class SmallCoffee extends RefinedCoffee {
public LargeCoffee(ICoffeeAdditives additives) {
super(additives);
}
@Override
public void orderCoffee(int count) {
additives.addSomething();
System.out.println(String.format("小杯咖啡%d杯",count));
}
}
//中杯
...
第四步:客户端调用
public static void main(String[] args) {
//点两杯加奶的大杯咖啡
RefinedCoffee largeWithMilk=new LargeCoffee(new Milk());
largeWithMilk.orderCoffee(2);
largeWithMilk.checkQuality();
}
输出结果:
加奶
大杯咖啡2杯
Milk 添加太多
通过使用桥接模式,就使得咖啡的容量和口味这两个维度可以独立变化,互不干扰。原本需要3x3=9个类的功能,现在只需要3+3=6 个类,有的同学就要说了,我去,加上那些基类和原来也差不多,没有多好啊?别急,等来了新需求你就知道哪个好了。新需求:增加一种加蜂蜜的口味,现在不使用桥接模式的类的个数是3x4=12,而使用了的是3+4=7,你说哪个好。
林蛋大终于完成了代码的重构,回头一看王者荣耀由于恶意挂机被扣除15个信用分,暂时不能进行排位赛,无奈只能再去巩固一下桥接模式了。
优点:
缺点:
经过此次事件,林蛋大对王二狗的敬佩又多了几分,二狗哥真乃神人也,不仅代码写的好,还能找到女朋友!
设计模式值得你刻意练习!
参考文章:
1:java设计模式之桥接模式