设计模式学习(一):Bridge桥接模式

简介: Bridge模式的作用是在“类的功能层次结构”和“类的实现层次结构”之间搭建桥梁。

一、什么是Bridge模式



Bridge模式的作用是在“类的功能层次结构”和“类的实现层次结构”之间搭建桥梁。


9edf803fedbc4a0fd898782ebbb4e8a3.png

1.1 类的功能层次结构


主要作用就是增加新的功能。当我们要增加新的功能时,我们可以从各个层次的类中找出最符合自己需求的类,然后以它为父类编写子类,并在子类中增加新的功能。这就是“类的功能层次结构”。


假设现在有一个类Something。当我们想在Something中增加新功能时(想增加一个具体方法时),会编写一个Something类的子类(派生类),即SomethingGood类。这样就构成了一个小小的类层次结构。

16091eb20f239fbf038834bcccee05de.png


这就是为了增加新功能而产生的层次结构。


如果我们要继续在SomethingGood类的基础上增加新的功能,我们可以同样地编写一个SomethingGood类的子类,即 SomethingBetter类。这样,类的层次结构就加深了。

e9852769e58d2d2a0de935f944581a01.png


当要增加新的功能时,我们可以从各个层次的类中找出最符合自己需求的类,然后以它为父类编写子类,并在子类中增加新的功能。这就是“类的功能层次结构”。


总而言之,父类具有基本功能,我们希望在子类中增加新的功能,这种层次结构就是“类的功能层次结构”。


需要注意的一点是:通常来说,类的层次结构关系不应当过深。


1.2 类的实现层次结构


主要作用就是增加新的实现。也就是说,这里的类的层次结构并非用于增加功能,并非用于方便我们增加新的方法,而是帮助我们实现这样的任务分担:父类通过声明抽象方法来定义接口(API ),子类通过实现具体方法来实现接口(API ),这种层次结构被称为“类的实现层次结构”。


例如,当子类Concreteclass实现了父类Abstractclass类的抽象方法时,它们之间就构成了一个小小的实现层次结构:

0194d735cc7834c45a2dafdd32273933.png

当我们以其他方式实现Abstractclass时,例如要实现一个AnotherConcreteclass时,类的层次结构会稍微发生一些变化:

938408233b8629b3a740b676d0e841f4.png

可以看到,我们不是为了增加新的功能,而是为了增加一种新的实现方式,我们继承了Abstractclass的子类,并实现了其中的抽象方法。这就是类的实现层次结构。


1.3 为什么要使用Bridge模式


前面已经介绍了类的功能层次结构与类的实现层次结构。那么,当我们想要编写子类时,就需要先确认自己的意图:“我是要增加功能呢?还是要增加实现呢?”


当类的层次结构只有一层时,功能层次结构与实现层次结构是混杂在一个层次结构中的。这样很容易使类的层次结构变得复杂,也难以透彻地理解类的层次结构。因为自己难以确定究竟应该在类的哪一个层次结构中去增加子类。因此,我们需要将“类的功能层次结构”与“类的实现层次结构”分离为两个独立的类层次结构。当然,如果只是简单地将它们分开,两者之间必然会缺少联系。所以我们还需要在它们之间搭建一座桥梁,Bridge模式的作用就是搭建这座桥梁。


二、Bridge模式示例代码



下面我们来看一段使用了Bridge模式的示例程序。这段示例程序的功能是“显示一些东西”。乍一听好像很抽象,不过随着我们逐渐地理解这段示例程序,也就能慢慢明白它的具体作用了。在例子中一定要注意体会类的层次结构。


先看一下所有类的作用:

5cdc75bbf005212ec775a85c2df2b6a1.png

类图:

3127317e67cd141b6511d6d41c84a49f.png


2.1 类的功能层次结构:Display类


Display类的功能是抽象的,负责“显示一些东西”。该类位于“类的功能层次结构”的最上层。


open、print、close这3个方法是Display类提供的接口(API),它们表示“显示的步骤”:


open是显示前的处理、print是显示处理、close是显示后的处理。

public class Display {
    private DisplayImpl impl;
    public Display(DisplayImpl impl) {
        this.impl = impl;
    }
    public void open() {
        impl.rawOpen();
    }
    public void print() {
        impl.rawPrint();
    }
    public void close() {
        impl.rawClose();
    }
    public final void display() {
        open();
        print();
        close();
    }
}


2.2 类的功能层次结构:CountDisplay类


CountDisplay类在Display类的基础上增加了一个新功能。Display类只具有“显示”的功能,CountDisplay类则具有“只显示规定的次数”的功能,这就是multiDisplay方法。CountDisplay类继承了Display类的open、print、 close方法,并使用它们来增加这个新功能。这就是“类的功能层次结构”。

public class CountDisplay extends Display{
    public CountDisplay(DisplayImpl impl) {
        super(impl);
    }
    //循环显示times次
    public void multiDisplay(int times) {
        open();
        for (int i = 0; i < times; i++) {
            print();
        }
        close();
    }
}


2.3 类的实现层次结构:Displaylmpl类


现在,我们来看桥的另外一侧——“类的实现层次结构”。

DisplayImpl类位于“类的实现层次结构”的最上层。


DisplayImpl类是抽象类,它声明了rawOpen、rawPrint、rawClose这3个抽象方法,它们分别与Display类的open、print、close方法相对应,进行显示前、显示、显示后处理。

public abstract class DisplayImpl {
    public abstract void rawOpen();
    public abstract void rawPrint();
    public abstract void rawClose();
}


2.4 类的实现层次结构:StringDisplaylmpl类


下面我们来看看真正的“实现”。StringDisplayImpl类是显示字符串的类。不过,它不是直接地显示字符串,而是继承了DisplayImpl类,作为其子类来使用raw0pen、rawPrint、rawClose方法进行显示。

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("+");
    }
}


2.5 用于测试的Main类

public class Main {
    public static void main(String[] args) {
        Display d1 = new Display(new StringDisplayImpl("Hello, China."));
        Display d2 = new CountDisplay((new StringDisplayImpl("Hello, World.")));
        CountDisplay d3 = new CountDisplay(new StringDisplayImpl("Hello, Universe."));
        d1.display();
        d2.display();
        d3.display();
        d3.multiDisplay(5);
    }
}


2.6 运行结果

+-------------+

|Hello, China.|

+-------------+

+-------------+

|Hello, World.|

+-------------+

+----------------+

|Hello, Universe.|

+----------------+

+----------------+

|Hello, Universe.|

|Hello, Universe.|

|Hello, Universe.|

|Hello, Universe.|

|Hello, Universe.|

+----------------+


三、拓展思路的要点



3.1 分开后更容易扩展


Bridge模式的特征是将“类的功能层次结构”与“类的实现层次结构”分离开了。将类的这两个层次结构分离开有利于独立地对它们进行扩展。


当想要增加功能时,只需要在“类的功能层次结构”一侧增加类即可,不必对“类的实现层次结构”做任何修改。而且,增加后的功能可以被“所有的实现”使用。


例如,我们可以将“类的功能层次结构”应用于软件所运行的操作系统上。如果我们将某个程序中依赖于操作系统的部分划分为Windows版、Macintosh版、Unix版,那么我们就可以用Bridge模式中的“类的实现层次结构”来表现这些依赖于操作系统的部分。也就是说,我们需要编写一个定义这些操作系统的共同接口(API)的Implementor角色,然后编写Windows版、Macintosh版、Unix版的3个ConcreteImplementor角色。这样一来,无论在“类的功能层次结构”中增加多少个功能,它们都可以工作于这3个操作系统上。


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


虽然使用“继承”很容易扩展类,但是类之间也形成了一种强关联关系,只要不修改代码,就无法改变这种关系,因此可以说它们之间形成了一种强关联关系。


如果想要很轻松地改变类之间的关系,使用继承就不适合了,因为每次改变类之间关系时都需要修改程序。这时,我们可以使用“委托”来代替“继承”关系。


上面的示例程序的Display类中使用了“委托”。Display类的impl字段保存了实现的实例。这样,类的任务就发生了转移:调用open方法会调用impl.rawOpen()方法、调用print方法会调用imp1.rawPrint()方法、调用close方法会调用impl.rawClose()方法。


也就是说,当其他类要求Display类“工作”的时候,Display类并非自己工作,而是将工作“交给impl”。这就是“委托”。


继承是强关联关系,但委托是弱关联关系。这是因为只有Display类的实例生成时,才与作为参数被传入的类构成关联。例如,在示例程序中,当Main类生成Display类和CountDisplay类的实例时,才将StringDisplayImpl的实例作为参数传递给Display类和 CountDisplay类。


如果我们不传递StringDisplayImpl类的实例,而是将其他Concretelmplementor角色的实例传递给Display类和CountDisplay类,就能很容易地改变实现。这时,发生变化的代码只有Main类,Display类和DisplayImpl类则不需要做任何修改。


继承是强关联关系,委托是弱关联关系。在设计类的时候,我们必须充分理解这一点。在Template Method模式中,也涉及到继承和委托的关系。


四、相关的设计模式



4.1 Template Method模式


在Template Method模式中使用了“类的实现层次结构”。父类调用抽象方法,而子类实现抽象方法。


设计模式学习(六):Template Method模板方法模式


4.2 Abstract Factory模式


为了能够根据需求设计出良好的ConcreteImplementor角色,有时我们会使用AbstractFactory模式。


设计模式学习(九):Abstract Factory抽象工厂模式


4.3 Adapter模式


使用Bridge模式可以达到类的功能层次结构与类的实现层次结构分离的目的,并在此基础上使这些层次结构结合起来。而使用Adapter模式则可以结合那些功能上相似但是接口 (API)不同的类。


设计模式学习(三):Adapter适配器模式


五、思考



5.1

题目:在示例程序中增加一个类,实现“显示字符串若干(随机)次”的功能。用于显示的方法是void randomDisplay(int times),它的作用是将字符串随机显示0~ times 次。要注意此时应当扩展哪个类。

答案:这是类的功能层次结构,应该继承Display类或者CountDisplay类。


5.2

题目:在示例程序中增加一个类,实现“显示文本文件的内容”的功能。要注意此时应当扩展哪个类。

答案:这是类的实现层次结构,应该继承DisplayImpl类。

相关文章
|
2月前
|
设计模式 PHP 开发者
PHP中的设计模式:桥接模式的解析与应用
在软件开发的浩瀚海洋中,设计模式如同灯塔一般,为开发者们指引方向。本文将深入探讨PHP中的一种重要设计模式——桥接模式。桥接模式巧妙地将抽象与实现分离,通过封装一个抽象的接口,使得实现和抽象可以独立变化。本文将阐述桥接模式的定义、结构、优缺点及其应用场景,并通过具体的PHP示例代码展示如何在实际项目中灵活运用这一设计模式。让我们一起走进桥接模式的世界,感受它的魅力所在。
|
3月前
|
设计模式 自然语言处理 算法
PHP中的设计模式:桥接模式的深入探索与应用
在PHP开发中,理解并运用设计模式是提升代码质量与可维护性的关键。本文聚焦于桥接模式——一种结构型设计模式,它通过封装一个抽象的接口,将实现与抽象分离,从而使得它们可以独立变化。不同于传统摘要的概述式表述,本文将以故事化的情境引入,逐步解析桥接模式的精髓,通过PHP代码示例详细展示其在实际项目中的应用,旨在为读者提供一个既深刻又易于理解的学习体验。
29 1
|
3月前
|
设计模式 Java
Java设计模式-桥接模式(9)
Java设计模式-桥接模式(9)
|
2月前
|
设计模式 Java
Java设计模式之桥接模式
这篇文章介绍了Java设计模式中的桥接模式,包括桥接模式的目的、实现方式,并通过具体代码示例展示了如何分离抽象与实现,使得两者可以独立变化。
47 0
|
4月前
|
设计模式 XML 存储
【七】设计模式~~~结构型模式~~~桥接模式(Java)
文章详细介绍了桥接模式(Bridge Pattern),这是一种对象结构型模式,用于将抽象部分与实现部分分离,使它们可以独立地变化。通过实际的软件开发案例,如跨平台视频播放器的设计,文章阐述了桥接模式的动机、定义、结构、优点、缺点以及适用场景,并提供了完整的代码实现和测试结果。桥接模式适用于存在两个独立变化维度的系统,可以提高系统的可扩展性和灵活性。
【七】设计模式~~~结构型模式~~~桥接模式(Java)
|
4月前
|
设计模式 缓存 项目管理
设计模式的基础问题之桥接模式在软件开发应用的问题如何解决
设计模式的基础问题之桥接模式在软件开发应用的问题如何解决
|
5月前
|
设计模式 JavaScript
js设计模式【详解】—— 桥接模式
js设计模式【详解】—— 桥接模式
74 6
|
6月前
|
设计模式 存储 算法
设计模式学习心得之五种创建者模式(2)
设计模式学习心得之五种创建者模式(2)
47 2
|
6月前
|
设计模式 Java 数据库
Java设计模式:桥接模式实现灵活组合,超越单一继承的设计之道(十)
Java设计模式:桥接模式实现灵活组合,超越单一继承的设计之道(十)
|
6月前
|
设计模式 Java
Java设计模式之桥接模式详解
Java设计模式之桥接模式详解