《一天一模式》— 桥接模式

1、桥接模式的概念

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

听懂了这句话就不用往下看了,说明你会了。设计模式

听不懂我以为也正常,若是用一句话能学会就没人看书了。像我这种笨人,都是学会了一个模式,而后往它的定义上套。函数

2、何时使用桥接模式

上面的概念中说到,抽象部分与它的实现(功能)部分分离,使它们均可以独立地变化。看下面这句话:测试

类的层级结构只有一层,功能层次结构实现层级结构是混杂在一个层级结构中时,你可使用桥接模式。 —— 《图解设计模式》this

在说以前先说三个概念:spa

  • 层的层级结构层数
  • 功能层次结构
  • 实现层级结构

可能名词比较陌生,我刚看的时候以为好高大上啊,没听过的技术名词都以为高大上。设计

但这三个名词用白话来描述就特别简单。3d

2.1类的层级结构的层数

用一句话就能说明白。code

类的层级结构就是类与子类之间的继承的层数。对象

这个图中类的层级结构的层数是:1

这个图中类的层级结构的层数是:2

类的层级结构的层数不能太深。

2.2 功能层次结构

类的功能层次结构,用一句话说不完,得两句话。

首先两个类有继承关系。

父类具备一些基本功能,在子类中添加了新的功能,这就叫功能层次结构。

仍是不太好理解,我当时是这么理解的,子类继承了父类的方法以外,还有本身的方法,这种层次结构叫功能层次结构。例以下面的图:

动物定义了呼吸方法,而后狗继承了动物,狗还加了一个犬吠方法,这种层次结构就叫功能层次结构。

2.3 实现层次结构

类的实现层次结构,也得用两句话说明。

首先两个类也有继承关系。

父类经过抽象的方式来定义方法。而后子类经过实现父类的抽象方法来完成这个方法的具体实现。

简单说就是一个类实现了父类的抽象方法。

动物定义了一个移动的抽象方法,狗实现了这个抽象方法,这种层次结构就叫实现层次结构。

2.4 总结

类的层级结构只有一层,功能层次结构与实现层级结构混杂在一个层级结构中,这样很容易让类的设计变得复杂,也难以透彻地理解类的层次结构。由于本身也难以肯定究竟在类的哪个层次结构去增长新需求。

这个时候,能够尝试使用桥接模式,将抽象部分与它的实现部分分离,使它们均可以独立地变化。

3、怎么使用桥接模式

简单说,就是怎么作抽象部分与它的实现部分分离。

下面会以一个场景来讲明,主要是两件事:

  1. 这个场景下不使用桥接模式会带来什么弊端;
  2. 怎么使用桥接模式来解决这个弊端;

先看来图来讲明:

类图以奔驰车为例作了一个模型,奔驰的车有不一样的车系,每一个车系均可以远程开门,而车系下的各个车的协议解析都不一样。因此在车系中定义了远程开关车门的抽象方法。由各个开门协议对象去实现本身的协议解析。

这种方法目前看没有什么问题,可是设计原则也好,设计模式也好,或者说设计工做,一个很重要任务就是要考虑到将来的需求变动。

那么以这个图做为假设,需求变化成还有Benz的B、D、E系列的车,每一个下面有2个具体车型,那么咱们就要加入3个抽象类,6个实现类,就比如下面的类图:

3.1 特定场景下不使用桥接模式带来的弊端

类爆炸

就如上图所示。上面的场景,从类的实现层次结构来说,每次实现的增长都成倍的增长类,最后会出现类爆炸的场面。加一个车系和对应的车型,就须要对应数量的类,好比B、D、E系列的车,每一个下面有5个具体车型,那么咱们就要加入3 * 5 = 15个类。

相同的例子还有不少,好比有用画国画举例的,画国画须要毛笔,要用到小号、中号、大号的毛笔,另外还须要水彩(12颜色)。组合起来是:颜色数量 * 毛笔型号数量 = 12 * 3 = 36,须要36(红色小号、红色中号、红色大号,绿色小号 …… 黑色大号)个类。

若是需求变动了,增长了2种毛笔的型号,颜色也增长了12种,那么本次新增的类就是2 * 24 = 48,一共须要5 * 24 = 120,共须要120个类,完成这个需求,这就是类爆炸!

难扩展

其次,上面的场景,想要实现一些新的功能,难以决定加到哪一层级。

由于这种设计,把类的功能扩展和类的多态实现混到了一块儿。混到一块儿的后果就是维护起来比较模糊。

例如:某些车要加一些新的功能,好比所有开门、前排开门、后排开门,而后又要加一些的新的实现,好比所有开门协议,开前门协议,看后门协议。这样代码混到了一块儿,职责不清晰,具体如图:

图中A系列的车系下的全部车型都有开门功能,所有开门功能,而后A200能够只开前门,A300能够只开后门。

在这种结构下扩展,只能将开门协议和开门动做放到一个类中,定义模糊。这就是所说的难以扩展。

3.2 如何使用桥接模式解决这个问题

通过桥接模式重构,获得上面的类图,将协议与车型分开,经过聚合的方式将车型与协议联接在一块儿,起到的重点是,分开后更容易扩展。当要增长新功能时,在类的功能层次结构(左侧,车型侧)增长便可,没必要对类的实现层次结构(右侧,协议侧)进行修改。反之亦然。

上面的例子中,若是新增了A系列车的具体车型,不须要增长协议类,若是增长了开门协议,也不须要增长车型类(B型车,C型车……同A型车)。

上一小节说到的国画需求也是同样,左侧能够是毛笔和3中型号的毛笔类,右侧是水彩和12种颜色的水彩类,加起来是15个类,添加毛笔型号不须要增长颜色一侧的类,添加颜色也不须要增长毛笔一次的类。

3.3 具体代码

测试Main方法,里面是桥接模式的使用方式

package cc.xuepeng;

/**
输出:
A系列开门协议解析。
奔驰-A100开门
A系列开门协议解析。
奔驰-A200开门
A系列开门协议解析。
奔驰-A100: 第1个门-开门。
奔驰-A100: 第2个门-开门。
奔驰-A100: 第3个门-开门。
奔驰-A100: 第4个门-开门。
*/
public class Client {

    public static void main(String[] args) {
        BenzA benzA100 = new BenzA100(new ProtocolBenzA(), "奔驰-A100");
        BenzA benzA200 = new BenzA200(new ProtocolBenzA(), "奔驰-A200");
        benzA100.openDoor();
        benzA200.openDoor();
        ((BenzA100) benzA100).openAllDoor();
    }

}

具体代码

// A系列奔驰车,类的功能结构层次的父类
public class BenzA {
    protected Protocol protocol;
    protected String name;

    // 在构造函数中,将协议对象注入进来,这里就是所谓的桥,经过这个protocol对象,能够调用到协议的方法
    public BenzA(Protocol protocol, String name) {
        this.protocol = protocol;
        this.name = name;
    }

    // 开车门时,先调用协议对象完成业务操做。
    public void openDoor() {
        protocol.resolveOpenDoorProtocol();
        System.out.println(name + "开门");
    }

}

public class BenzA100 extends BenzA {

    public BenzA100(Protocol protocol, String name) {
        super(protocol, name);
    }

    // 若是有特定的方法,能够从功能结构层侧一侧直接扩展。
    public void openAllDoor() {
        super.protocol.resolveOpenDoorProtocol();
        for (int i = 1; i <= 4; i++) {
            System.out.println(super.name + ": 第" + i + "个门-开门。");
        }
    }

}

public class BenzA200 extends BenzA {

    public BenzA200(Protocol protocol, String name) {
        super(protocol, name);
    }

}

// A系列车的协议解析类,类的实现层次结构的父类
public abstract class Protocol {

    public abstract void resolveOpenDoorProtocol();

}

public class ProtocolBenzA extends Protocol {

    public void resolveOpenDoorProtocol() {
        System.out.println("A系列开门协议解析。");
    }

}

4、总结

桥接模式,从代码编写的角度来看,就是把一个类结构拆分红了两个类结构,一边负责经过类的继承特定添加功能。另外一边编写这些功能的具体实现。而后经过成员变量将实现传给功能。这样在扩展功能时,实现侧不须要修改,变动实现时,功能侧不须要修改。

从设计角度来看,要求咱们设计时先从业务维度出发去思考,考虑后续需求变动是否会使类的结构变得更复杂。

若是业务复杂(类型的子类较多,实现功能也各有不一样),而且后续的变动或者新功能会很频繁,那么就考虑使用桥接模式,将类的功能层次与类的实现层次拆分开,而后以包含(Has a)的方式创建功能层次与实现层侧的关系,便可在后续有需求变动时获得如下几点好处:

  • 代码结构更清晰;
  • 业务结构更明确;
  • 减小类的数量;

以上就是我对桥接模式的一些理解,有不足之处请你们矫正,谢谢。

相关文章
相关标签/搜索