桥接模式

286 阅读7分钟

桥接模式

桥接模式(Bridge)能将抽象与实现分离,使二者可以各自单独变化而不受对方约束,使用时再将它们组合起来,就像架设桥梁一样连接它们的功能,如此降低了抽象与实现这两个可变维度的耦合度,以保证系统的可扩展性。

形与色的纠葛

既然基础建设如此重要,那么我们就用实例来分析一下桥接模式下的产业分工与合作。假设我们要画一幅抽象画,它主要由各种形状的色块组成,以此来表达世界的多样性

要完成这幅作品,不同颜色的画笔是必不可少的工具,那么相应地我们就得定义这些画笔工具类。首先抛开画笔的颜色,画笔本身一定是类似的,所以我们定义一个画笔抽象类.

package bridge;

public abstract class Pen {
    public abstract void getColor();
    public void draw(){
        getColor();
        System.out.println("▲");
    }
}

画笔抽象类在第3行定义了抽象方法getColor()获取颜色,并交给子类实现不同的颜色;接着在第5行绘图方法draw()中先调用getColor()以获取具体的颜色,然后画出一个三角形。下面我们来看具体的黑色画笔类BlackPen。

package bridge;

public class BlackPen extends Pen {
    @Override
    public void getColor() {
        System.out.println("黑");
    }
}

黑色画笔类在第4行实现了获取颜色方法getColor(),并输出了字符串“黑”,以此来模拟黑色墨水的输出。我们先不急于进行过多的扩展,至少目前已经足以进行作画了,我们用客户端试着运行一下

package bridge;

public class Client {
    public static void main(String[] args) {
        Pen blackPen = new BlackPen();
        blackPen.draw();
        //输出:黑▲
    }
}

我们在调用黑色画笔类的绘图方法draw()后成功输出了“黑△”(黑色三角形)。同理,我们可以继续定义白色画笔类,画出“白△”(白色三角形)。然而,不管我们制造多少种颜色的画笔,都只能画出三角形,这是因为我们在抽象类里硬编码了对“△”的输出,这就造成了形状被牢牢地捆绑于各类彩色画笔中,对于其他形状的绘制则无能为力,使系统丧失了灵活性与可扩展性。

架构产业链

我们已经利用画笔的抽象实现了颜色的多态,现在要解决的问题是对形状的抽离,将形状与颜色彻底分离开来,使它们各自扩展。既然颜色是由画笔来决定的,那么形状可以依赖尺子来规范其笔触线条走向。我们设想这样一个场景,画笔与尺子这两种工具分别产于南北两座孤岛,北岛擅长制造各色画笔,南岛则擅长制造各种形状的尺子

按照这种模式我们开始规划南岛文具产业。首先我们把可以规范形状的尺子类从画笔产业中独立出来,它们至少能够画出正方形、三角形和圆形,来看看南岛所制造的尺

尺子的功能是对笔触线条走向进行规范。为了让尺子各尽其能而不至于毫无章法地扩展,我们先定义一个尺子的高层接口

package bridge;

public interface Ruler {
    public void regularize();
}

尺子接口定义了笔触线条走向规范方法regularize(),为各种形状的尺子实现留好了接口。为保持简单,我们在这里忽略形状的大小,假设一种形状对应一个类,那么应该有正方形尺子类、三角形尺子类以及圆形尺子类

package bridge;

public class SquareRuler implements Ruler {
    @Override
    public void regularize() {
        System.out.println("|正方形|");
    }
}
package bridge;

public class TriangleRuler implements Ruler {
    @Override
    public void regularize() {
        System.out.println("|三角形|");
    }
}
package bridge;

public class CircleRuler implements Ruler {
    @Override
    public void regularize() {
        System.out.println("|圆|");
    }
}

南岛文具产业已经被规划完成。接着我们来看处于产业链另一端的北岛文具产业,其擅长制造的是彩色画笔

依照南、北岛的产业合作模式,我们同样需要对北岛产业进行重新规划,也就是对之前的画笔类相关代码进行重构。因为画笔必须有尺子的协助才能完成漂亮的画作,所以我们假设北岛制造处于“产业链下游”,修改之前的画笔抽象类,使其能够用到尺子

package bridge;

public abstract class Pen {
    protected Ruler ruler;//尺子的引用
    public Pen(Ruler ruler){
        this.ruler = ruler;
    }
    public abstract void draw();//抽象方法
}
package bridge;

public class BlackPen extends Pen {
    public BlackPen(Ruler ruler) {
        super(ruler);
    }

    @Override
    public void draw() {
        System.out.print("黑");
        ruler.regularize();
    }
}
package bridge;

public class WhitePen extends Pen {
    public WhitePen(Ruler ruler) {
        super(ruler);
    }

    @Override
    public void draw() {
        System.out.print("白");
        ruler.regularize();
    }
}

客户端Client

package bridge;

public class Client {
    public static void main(String[] args) {
        //白笔
        new WhitePen(new CircleRuler()).draw();
       new WhitePen(new SquareRuler()).draw();
       new WhitePen(new TriangleRuler()).draw();

       //黑笔
        new BlackPen(new CircleRuler()).draw();
        new BlackPen(new SquareRuler()).draw();
        new BlackPen(new TriangleRuler()).draw();
    }
}

笛卡儿积

在桥接的产业合作模式下,南、北岛勤劳的工人们继续扩大生产,制造了更多样式的尺子和画笔,让客户端能够更自由地作画。通过例程我们可以看到,桥接模式将原本对形状的继承关系改为聚合(组合)关系,使形状实现从颜色中分离出来,最终完成多类组件维度上的自由扩展与拼装,使形与色的自由搭配成为可能。

我们的例子其实比较简单,只是2色3形的笛卡儿积组合,如果再加入更多的颜色与形状,笛卡儿积的结果数量会大得惊人。举个例子,我们现有7种颜色和10种形状,组合起来就有70(7×10)种可能,假如设计程序时我们只用继承的方式去实现每种可能,那么至少需要70个类。如果颜色与形状不断增多,系统可能会出现代码冗余以及类泛滥的情况,之后每加一种颜色或形状都将举步维艰,系统扩展工作将会是一场灾难。如果利用桥接模式的设计,我们只需要17(7+10)个类便可以组装成任意可能了,并且之后对任何维度的扩展也是轻而易举的。

多姿多彩的世界

桥接模式构架了一种分化的结构模型,巧妙地将抽象与实现解耦,分离出了2个维度(尺子与画笔)并允许其各自延伸和扩展,最终使系统更加松散、灵活,请参看桥接模式的类结构

我们可以把桥接模式分为“抽象方”与“实现方”2个维度阵营,其中各角色的定义如下。

Abstraction(抽象方):抽象一方的高层接口,多以抽象类形式出现并持有实现方的接口引用,对应本章例程中的画笔类。

AbstractionImpl(抽象方实施):继承自抽象方的具体子类实现,可以有多种实施并在抽象方维度上自由扩展,对应本章例程中的黑色画笔和白色画笔。

Implementor(实现方):实现一方的接口规范,从抽象方中剥离出来成为另一个维度,独立于抽象方并不受其干扰,对应本章例程中的尺子接口。

ConcreteImplementor(实现方实施):实现一方的具体实施类,可以有多个实施并在实现方维度上自由扩展,对应本章例程中的正方形尺子类、三角形尺子类、圆形尺子类。

经济发展靠分工,系统扩展靠抽离,桥接模式将抽象与实现彻底解耦,使形状与颜色的纠葛终被化解,各自为营,互不侵扰。劳动分工实现了各种产品制造的自由扩展,使其能够在各自维度上达成多态,无限延伸。桥梁作为经贸发展的纽带更是不可或缺,它让贸易双方各尽其能,并达到合作共赢的状态。产业链的形成则使原本的产品再次组合,具备更多的功能。多姿多彩的世界,一定离不开形形色色的自由组合。

Go版本代码

package bridge

import "fmt"

type Ruler interface {
    regularize()
}
type CircleRuler struct{}

func (c *CircleRuler) regularize() {
    fmt.Println("|圆|")
}

type SquareRuler struct {
}

func (s *SquareRuler) regularize() {
    fmt.Println("|正方形|")
}

type TriangleRuler struct {
}

func (t *TriangleRuler) regularize() {
    fmt.Println("|三角形|")
}
package bridge

import "fmt"

type Pen struct {
    Ruler
    Draw func()
}

type BlackPen Pen

func NewBlackPen(ruler Ruler) *BlackPen {
    return &BlackPen{
        Ruler: ruler,
        Draw: func() {
            fmt.Print("黑")
            ruler.regularize()
        },
    }
}

type WhitePen Pen

func NewWhitePen(ruler Ruler) *WhitePen {
    return &WhitePen{
        Ruler: ruler,
        Draw: func() {
            fmt.Print("白")
            ruler.regularize()
        },
    }
}
package main

import "desginPatterns/bridge"

func main() {
    bridge.NewWhitePen(&bridge.CircleRuler{}).Draw()
    bridge.NewWhitePen(&bridge.SquareRuler{}).Draw()
    bridge.NewWhitePen(&bridge.TriangleRuler{}).Draw()

    //黑笔
    bridge.NewBlackPen(&bridge.SquareRuler{}).Draw()
    bridge.NewBlackPen(&bridge.CircleRuler{}).Draw()
    bridge.NewBlackPen(&bridge.TriangleRuler{}).Draw()
}

行为篇