桥接模式 -- 分离功能层次结构和实现层次结构

1. 概述

桥接模式(Bridge Pattern)是一种结构型设计模式。在理解桥接模式前,我们要先能够区分“类的功能层次结构”和“类的实现层次结构”。

举一个简单的例子,比如我们设计了一个程序,它有打印字符串的功能,同时它又需要分别能够在 windows 和 linux 上运行,并且打印不同的内容。那么假定我们有一个 Program 类,类中有一个print方法,那么我们会在 ProgramWinImpl 和 ProgramLiunxImpl 中分别实现 print 方法用于打印不同的内容,这就是我们上面说到的“类的实现层次结构”的拓展。假定现在我们需要给这个程序增加一个新功能 multiPrint,用于打印多行字符串,那么我们就会创建一个 MutilProgram 来继承 Program,并且增加 multiPrint 这个方法。这就是我们上面说到的“类的功能层次结构”的拓展。如果对这两种不同维度的拓展都使用简单的继承思想,那么我们的继承关系将会非常混杂、难以理解。这时,我们就需要引入桥接模式,帮助我们搭建“类的功能层次结构”和“类的实现层次结构”之间的桥梁。

2. 模式类图

bridge-1.png

3. 模式角色

Abstraction (抽象化)
Abstraction 角色位于“类的功能层次结构”的最上层。该角色保存了Implementor角色的实例,使用Implementor角色的方法定义基本功能。

RefinedAbstraction (改善的抽象化)
RefinedAbstraction 角色负责在基础上增加新的功能。

Implementor (实现者)
Implementor 角色位于“类的实现层次结构”的最上层。它定义了用于实现 Abstraction 角色的接口的方法。

ConcreteImplementor (具体实现者)
ConcreteImplementor 角色负责实现 Implementor 角色中定义的方法。

4. 代码示例

类的功能层次结构

Display.java

Display 类是“类的功能层次结构”的最上层,表示功能的抽象。它的 impl 字段保存了实现 Display 类的具体功能的实例。该实例通过 Display 的构造函数被传递给 Display 类,保存在 impl 字段中。impl 字段就是类的两个层次结构的“桥梁”。

public class Display {
    private DisplayImpl impl;

    public Display(DisplayImpl impl) {
        this.impl = impl;
    }

    public void open() {
        this.impl.rawOpen();
    }

    public void print() {
        this.impl.rawPrint();
    }

    public void close() {
        this.impl.rawClose();
    }

    public void display() {
        open();
        print();
        close();
    }
}

CountDisplay.java

CountDisplay 类是 Display 类的功能层次结构的拓展,它增加了“多次显示”的功能。

public class CountDisplay extends Display {
    public CountDisplay(DisplayImpl impl) {
        super(impl);
    }

    public void multiDisplay(int times) {
        open();
        for (int i = 0; i < times; i++) {
            print();
        }
        close();
    }
}

类的实现层次结构

DisplayImpl.java

DisplayImpl 是一个抽象类,只声明的对应 Display 类的三个抽象方法。

public abstract class DisplayImpl {
    public abstract void rawOpen();

    public abstract void rawPrint();

    public abstract void rawClose();
}

StringDisplayImpl.java

StringDisplayImpl 类最后实现了 Display 的所有功能。

public class StringDisplayImpl extends DisplayImpl {
    private String string;
    private int width;

    public StringDisplayImpl(String string) {
        this.string = string;
        this.width = string.getBytes().length;
    }

    @Override
    public void rawOpen() {
        printLine();
    }

    @Override
    public void rawPrint() {
        System.out.println("|" + string + "|");
    }

    @Override
    public void rawClose() {
        printLine();
    }

    private void printLine() {
        System.out.print("+");
        for (int i = 0; i < width; i++) {
            System.out.print("-");
        }
        System.out.println("+");
    }
}

Main.java

最后我们看一下如何调用 Display。

public class Main {
    public static void main(String[] args){
        Display d1 = new Display(new StringDisplayImpl("hello world"));
        CountDisplay d2 = new CountDisplay(new StringDisplayImpl("hello world"));

        d1.display();
        d2.display();
        d2.multiDisplay(5);
    }

}

结果

+-----------+
|hello world|
+-----------+
+-----------+
|hello world|
+-----------+
+-----------+
|hello world|
|hello world|
|hello world|
|hello world|
|hello world|
+-----------+

5. 延展阅读

继承是强关联,委托是弱关联

继承是一种拓展类的有效手段,但是其弊端就是使父类和子类之间有很强的关联性。如果我们想灵活的改变类之间的关系,那么使用“委托”的概念就会显得更合适。

以我们的代码示例为例,所谓“委托”,就是指 Display 类自身的方法并不自己直接处理逻辑,而是交由(委托) DisplayImpl 实现类来处理。

public void open() {
    this.impl.rawOpen();
}

而 DisplayImpl 类实在 Display 类的实例生成时才被作为参数传入的,所以 Display 类中方法的最终实现可以在此刻才被决定。这就是通过“委托”实现类功能的自由拓展。

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

推荐阅读更多精彩内容

  • 1.初识桥接模式 将抽象部分与它的实现部分分离,使它们都可以独立地变化。 Abstraction:抽象部分的接口。...
    王侦阅读 900评论 0 7
  • 学习的过程中发现这两个概念真的是有点区分不开,尽管可以很感性的说bridge模式要比strategy模式更复杂更具...
    小陈阿飞阅读 1,900评论 0 1
  • 结构模式(Structural Pattern)描述如何将类或者对象结合在一起形成更大的结构。 类的结构模式:(静...
    苏先生Tongson阅读 1,766评论 1 0
  • 桥接模式(结构型) 一、桥接模式概述 桥接模式是一种很实用的结构型设计模式,如果软件系统中某个类存在两个独立变化的...
    哈哈大圣阅读 361评论 0 0
  • 设计模式 一、引言 假如有三个品牌的手机vivo,oppo和小米,如果手机手机壳一体生产,会是这样的: 对应到相应...
    凯玲之恋阅读 7,294评论 0 8