【设计模式(七)】结构型模式之桥接模式

个人学习笔记分享,当前能力有限,请勿贬低,菜鸟互学,大佬绕道

如有勘误,欢迎指出和讨论,本文后期也会进行修正和补充


前言

设想如果要绘制矩形、圆形、椭圆、正方形4个形状,且各有红、黄、蓝3种颜色,那么显然一共有12种不同的结果

如果有m个形状,n个颜色,那么需要定义m*n个类,看上去可以接受是吗?

那如果要新增一种颜色,就得多定义n个类,同理增加一种形状也需要增加m个类,修改的话同理也要修改大量的类

这个维护成本可就高的过分了,耦合度太高,而且违背了单一职责原则,那咋办嘛?

换个思路,其实可以将形状与颜色分离开,再将其将其进行组合,即可得到需要的结果

这样一来,形状和颜色从继承关系变成了关联关系,因此将可以独立变化,互不影响,那么新增形状或颜色都只需要增加一个类,变更也只需要修改一个类

万事分个主次,我们不妨将形状为主,颜色为次,颜色作为形状的一种属性

同理,形状也可以拥有其它属性,比如渐变度、透明度、边框等等,都可以用桥接模式组合在一起,且独立变化


桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。

这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。


1.介绍

使用目的:将抽象部分与它的实现部分分离开来,使他们都可以独立变化

使用时机:实现系统可能有多个角度分类,每一种角度都可能变化,

解决问题:在有多种可能会变化的情况下,用继承会造成类爆炸问题,扩展起来不灵活。

实现方法:把这种多角度分类分离出来,让它们独立变化,减少它们之间耦合

应用实例

  1. 奶茶会有主材、温度、配料等多个独立的选项,互不影响
  2. Timer().schedule(new TimerTask(){}),需要传入一个TimerTask对象,我们可以对其进行自定义,只要保持对象的类继承TimerTask就行了

优点

  1. 抽象和实现的分离。
  2. 优秀的扩展能力。
  3. 实现细节对客户透明。

缺点

  1. 提高了系统的抽象程度,也就一定程度上增加系统复杂度
  2. 由于聚合关联关系建立在抽象层,开发者需要针对抽象进行设计与编程
  3. 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。


简而言之,就是避免继承关系,转而建立关联关系,进而脱耦,再进行组合


概念补充

鉴于出现了一些较为抽象的概念,这里进行一下补充解释

  • 抽象化:将复杂物体的一个或几个特性抽出去而只注意其他特性的行动或过程。在面向对象就是将对象共同的性质抽取出去而形成类的过程

    比如在前言中,我们将颜色抽离出去,而只在意形状,也就是将颜色抽象化了

  • 实现化:针对抽象化给出的具体实现。它和抽象化是一个互逆的过程,实现化是对抽象化事物的进一步具体化。

    虽然将颜色抽离出去了,但是每种颜色依然需要实现,生成自己类,也就是将颜色实现化

  • 脱耦:脱耦就是将抽象化和实现化之间的耦合解脱开,或者说是将它们之间的强关联改换成弱关联,将两个角色之间的继承关系改为关联关系

    我们将颜色和形状分离开了,使得两者可以独立变化,只需要保证能对接上即可,也就是将颜色与形状脱耦

桥接模式中的所谓脱耦,就是指在一个软件系统的抽象化和实现化之间使用关联关系(组合或者聚合关系)而不是继承关系,从而使两者可以相对独立地变化,这就是桥接模式的用意。


2.结构

桥接模式通常包括4个主要角色

  • 抽象化(Abstraction)角色:定义抽象类,并包含一个对实现化对象的引用。
  • 扩展抽象化(Refined Abstraction)角色:是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。
  • 实现化(Implementor)角色:定义实现化角色的接口,供扩展抽象化角色调用。
  • 具体实现化(Concrete Implementor)角色:给出实现化角色接口的具体实现。
image-20201021144635643
  • 客户端调用抽象类Shape(抽象化角色)
    • 抽象类Shape持有接口类ColorBorder,作为其两项属性(实现化角色)
      • 实体类BlueRed实现Color,作为具体的颜色属性(具体实现化角色)
      • 实体类NormalBorder实现Border,作为具体的边框属性(具体实现化角色)
    • 实体类CircleSquareRectangle继承Shape,并实现抽象方法,作为具体的实体(扩展抽象化角色)

Shape、Color、Border自身均无法实例化,我们需要实例化各个具体属性实体,并组装在一起,进而构成一个整体


3.实现

模拟业务如下:

  • 需要目标角色包括两个属性,颜色Color和形状Shape,并拥有方法draw()输出当前角色信息
  • 颜色已有两种RedBlue
  • 形状已有两种CircleSquare

我们将Shape作为抽象化角色,Color作为实现化角色

==实际应用中根据需求角色哪种属性作为抽象化角色,其余属性作为实现化角色==

  1. 定义接口Color(实现化角色)

    interface Color {
        String drawColor();
    }
    
  2. 定义类RedBlue,实现Color(具体实现化角色)

    class Red implements Color {
        @Override
        public String drawColor() {
            return "red";
        }
    }
    
    class Blue implements Color {
        @Override
        public String drawColor() {
            return "blue";
        }
    }
    
  3. 定义抽象类Shape(抽象化角色)

    abstract class Shape {
        protected Color color;
    
        public Shape(Color color) {
            this.color = color;
        }
    
        abstract void draw();
    }
    

    请注意,这里使用构造函数引用color,也可以使用别的方法,如提供setShape(Color color)方法等均可

  4. 定义类CircleSquare(扩展抽象化角色)

    class Circle extends Shape {
    
        public Circle(Color color) {
            super(color);
        }
    
        @Override
        void draw() {
            System.out.println("draw a circle with " + color.drawColor());
        }
    }
    
    class Square extends Shape {
    
        public Square(Color color) {
            super(color);
        }
    
        @Override
        void draw() {
            System.out.println("draw a square with " + color.drawColor());
        }
    }
    
  5. 客户端调用

    public class BridgeTest {
        public static void main(String[] args) {
            Shape redCircle = new Circle(new Red());
            redCircle.draw();
    
            Shape blueSquare = new Square(new Blue());
            blueSquare.draw();
        }
    
    }
    

完整代码

package com.company.test.bridge;

interface Color {
    String drawColor();
}

class Red implements Color {
    @Override
    public String drawColor() {
        return "red";
    }
}

class Blue implements Color {
    @Override
    public String drawColor() {
        return "blue";
    }
}

abstract class Shape {
    protected Color color;

    public Shape(Color color) {
        this.color = color;
    }

    abstract void draw();
}

class Circle extends Shape {

    public Circle(Color color) {
        super(color);
    }

    @Override
    void draw() {
        System.out.println("draw a circle with " + color.drawColor());
    }
}

class Square extends Shape {

    public Square(Color color) {
        super(color);
    }

    @Override
    void draw() {
        System.out.println("draw a square with " + color.drawColor());
    }
}

public class BridgeTest {
    public static void main(String[] args) {
        Shape redCircle = new Circle(new Red());
        redCircle.draw();

        Shape blueSquare = new Square(new Blue());
        blueSquare.draw();
    }

}

运行结果

image-20201021151849099

4.扩展使用

桥接模式与适配器模式的联用

桥接模式可以将多种属性组装在一起,分离为抽象化角色和实现化角色,那如果无法兼容呢?

比如调用了大量第三方接口,甚至是开发者之间未提前约定好接口,都会出现不兼容的情况

这个时候就可以使用适配器模式,对接口进行转换,使其能够兼容,进而在一起协同工作

在开发过程中,也可以使用缺省适配器来规范接口,暂未实现的可以缺省


后记

桥接模式的核心思想就是使用关联关系替换继承关系,从而将一个整体分成不同的组件,而不是不同层级

好处是脱耦,便于扩展和维护

这种思想才是我们学习的重点,能帮助我们写出更优质的代码,而不仅仅是个代码的搬运工而已


作者:Echo_Ye

WX:Echo_YeZ

Email :echo_yezi@qq.com

个人站点:在搭了在搭了。。。(右键 - 新建文件夹)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342