秒懂设计模式之桥接模式(Bridge Pattern)

前言

人在IT江湖飘,不懂设计模式咋装X?

桥接模式在日常开发中不是特别常用,主要是因为上手难度较大,但是对于理解面向对象设计有非常大的帮助。

定义

桥接模式是将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interfce)模式。

使用场景

我们大家都熟悉,顾名思义就是用来将河的两岸联系起来的。而此处的桥是用来将两个独立的结构联系起来,而这两个被联系起来的结构可以独立的变化,所有其他的理解只要建立在这个层面上就会比较容易。

下面是一些官方的说明,比较晦涩,必须等你有一定的经验后才能理解: 1. 如果一个系统需要在抽象化和具体化之间增加更多的灵活性,避免在两个层次之间建立静态的继承关系,通过桥接模式可以使它们在抽象层建立一个关联关系。

  1. “抽象部分”和“实现部分”可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将一个抽象化子类的对象和一个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合。

  2. 一个类存在两个(或多个)独立变化的维度,且这两个(或多个)维度都需要独立进行扩展。

  3. 对于那些不希望使用继承或因为多层继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。

如何实现

业务场景

在讲策略模式的时候,王二狗和牛翠花不是要到天津之眼去约炮,不,约会嘛,两人到那后先去星巴克喝咖啡了,星巴克提供了多种选择,从容量上说有大杯,中杯,小杯,从口味上说有原味,加糖,这可难为了有选择恐惧症的牛翠花,半天点不出来,后面的人都开始骂娘了,王二狗也只能陪着笑脸道歉。 其实被难为的除了牛翠花,还有给星巴克做订单系统的外包公司的程序员林蛋大。一开始提需求的时候星巴克说我们只有正常杯(中杯),原味和加糖这几种选择,人家林蛋大也是有两年工作经验的码农,这需求不在话下,蛋大还想到了要面对抽象编程。

首先定义一个点咖啡接口,里面有一个下单方法,至于点哪种口味的咖啡,就由其子类去决定,完美,蛋大好牛逼啊!

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));
    }
}
。。。

加着加着蛋大慌了,共需要3x2=6个类啊,大杯原味和加糖,中杯原味和加糖,小杯原味和加糖。过段时间万一那二笔客户又要出加奶,加蜂蜜等等口味,说不定还有迷你杯,女神杯等等规格的咖啡,那我这边的类不就爆炸了吗?看来的去找个设计模式了。。。

此场景桥接模式正合适,这里有两个变化维度,咖啡的容量和口味,而且都需要独立变化。如果使用继承的方式,随着变化类就会急剧的增加。你可以将容量理解为抽象部分,而口味理解为实现部分,这两个部分需要桥接。

使用桥接模式

桥接模式UML 图如下

image.jpg

桥梁模式所涉及的角色有:

  • 抽象化(Abstraction)角色:抽象化给出的定义,并保存一个对实现化对象的引用。
  • 修正抽象化(RefinedAbstraction)角色:扩展抽象化角色,改变和修正父类对抽象化的定义。
  • 实现化(Implementor)角色:这个角色给出实现化角色的接口,但不给出具体的实现。必须指出的是,这个接口不一定和抽象化角色的接口定义相同,实际上,这两个接口可以非常不一样。实现化角色应当只给出底层操作,而抽象化角色应当只给出基于底层操作的更高一层的操作。
  • 具体实现化(ConcreteImplementor)角色:这个角色给出实现化角色接口的具体实现。

抽象化角色就像是一个水杯的手柄,而实现化角色和具体实现化角色就像是水杯的杯身。手柄控制杯身,这就是此模式别名“柄体”的来源。

林蛋大查了半天不知道使用哪个设计模式,只能求助前辈王二狗了。电话通了:狗哥,我这有个问题。。。王二狗:我这正解决终身大事呢,没工夫理你,你去调查一下桥接模式。林蛋大:不愧是大神,改天请你和嫂子吃饭,先挂啦。挂电话后,蛋大就开始用桥接模式重构代码了。

蛋大分析了当前业务场景,认为可以将咖啡的容量作为抽象化Abstraction,而咖啡口味为实现化Implementor

第一步:创建抽象化部分:

//抽象化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()方法而组合起来。

下面是一个对抽象化修正的一个类,里面增加了一个品控的方法

//RefinedAbstraction
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()?"太多":"正常"));
    }
}

第二步:创建实现化部分

public interface ICoffeeAdditives {
    void addSomething();
}
//加奶
public class Milk implements ICoffeeAdditives {
    @Override
    public void addSomething() {
        System.out.println("加奶");
    }
}
//加糖
public class Sugar implements ICoffeeAdditives {
    @Override
    public void addSomething() {
        System.out.println("加糖");
    }
}

第三步:客户端调用

public static void main(String[] args) {
        //点两杯加奶的大杯咖啡
        RefinedCoffee largeWithMilk=new LargeCoffee(new Milk());
        largeWithMilk.orderCoffee(2);
        largeWithMilk.checkQuality();
    }

输出结果:

加奶
大杯咖啡2杯
Milk 添加太多

通过使用桥接模式,就使得咖啡的容量和口味这两个维度可以独立变化,互不干扰。林蛋大终于完成了代码的重构,回头一看王者荣耀由于恶意挂机被扣除15个信用分,暂时不能进行排位赛,无奈只能再去巩固一下桥接模式了。

优缺点

优点:

  • 分离抽象接口及其实现部分。桥接模式使用“对象间的关联关系”解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。所谓抽象和实现沿着各自维度的变化,也就是说抽象和实现不再在同一个继承层次结构中,而是“子类化”它们,使它们各自都具有自己的子类,以便任何组合子类,从而获得多维度组合对象。
  • 在很多情况下,桥接模式可以取代多层继承方案,多层继承方案违背了“单一职责原则”,复用性较差,且类的个数非常多,桥接模式是比多层继承方案更好的解决方法,它极大减少了子类的个数。
  • 桥接模式提高了系统的可扩展性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统,符合“开闭原则”。

缺点:

  • 桥接模式的使用会增加系统的理解与设计难度,由于关联关系建立在抽象层,要求开发者一开始就针对抽象层进行设计与编程。
  • 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性,如何正确识别两个独立维度也需要一定的经验积累。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,384评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,845评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,148评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,640评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,731评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,712评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,703评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,473评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,915评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,227评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,384评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,063评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,706评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,302评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,531评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,321评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,248评论 2 352

推荐阅读更多精彩内容

  • 【学习难度:★★★☆☆,使用频率:★★★☆☆】直接出处:桥接模式梳理和学习:https://github.com/...
    BruceOuyang阅读 899评论 0 2
  • 1.初识桥接模式 将抽象部分与它的实现部分分离,使它们都可以独立地变化。 Abstraction:抽象部分的接口。...
    王侦阅读 909评论 0 7
  • 一、应用场景 设想如果要绘制矩形、圆形、椭圆、正方形,我们至少需要4个形状类,但是如果绘制的图形需要具有不同的颜色...
    QuantRuu阅读 783评论 0 51
  • 原文传送门 1 介绍 桥接模式是对象的结构模式。又称为柄体(Handle and Body)模式或接口(Inter...
    dd299阅读 504评论 0 1
  • 一个懂得感恩的人,一定有一颗善良的心,一定会有福报。感恩我的父母把我带到世上,给予我最宝贵的生命,感恩我的奶奶含辛...
    包包_3a0d阅读 182评论 0 1