面向对象设计的设计模式(九):桥接模式

定义

桥接模式(Simple Factory Pattern):将抽象部分与它的实现部分分离,使它们都可以独立地变化。

定义解读:桥接模式的核心是两个抽象以组合的形式关联到一起,从而他们的实现就互不依赖了。

适用场景

如果一个系统存在两个独立变化的维度,而且这两个维度都需要进行扩展的时候比较适合使用桥接模式。

下面来看一下桥接模式的成员和类图。

成员与类图

成员

桥接模式一共只有三个成员:

  • 抽象类(Abstraction):抽象类维护一个实现部分的对象的引用,并声明调用实现部分的对象的接口。
  • 扩展抽象类(RefinedAbstraction):扩展抽象类定义跟实际业务相关的方法。
  • 实现类接口(Implementor):实现类接口定义实现部分的接口。
  • 具体实现类(ConcreteImplementor):具体实现类具体实现类是实现实现类接口的对象。

下面通过类图来看一下各个成员之间的关系:

模式类图

桥接模式类图

从类图中可以看出Abstraction持有Implementor,但是二者的实现类互不依赖,这就是桥接模式的核心。

代码示例

场景概述

创建一些不同的形状,这些形状带有不同的颜色:

三种形状:

  • 正方形
  • 长方形
  • 原型

三种颜色:

  • 红色
  • 绿色
  • 蓝色

场景分析

根据上述需求,可能有的朋友会这么设计:

  • 正方形(父类)
    • 红色正方形
    • 绿色正方形
    • 蓝色正方形
  • 长方形(父类)
    • 红色长方形
    • 绿色长方形
    • 蓝色长方形
  • 圆形(父类)
    • 红色圆形
    • 绿色圆形
    • 蓝色圆形

这样的设计确实可以实现上面的需求。但是设想一下,如果后来增加了一种颜色或者形状的话,是不是要多出来很多类?如果形状的种类数是m,颜色的种类数是n,以这种方式创建的总类数就是 m*n,当m或n非常大的时候,它们相乘的结果就会变得很大。

我们观察一下这个场景:形状和颜色这二者的是没有关联性的,二者可以独立扩展和变化,这样的组合比较适合用桥接模式来做。

根据上面提到的桥接模式的成员:

  • 抽象类就是图形的抽象类
  • 扩展抽象类就是继承图形抽象类的子类:各种形状
  • 实现类接口就是颜色接口
  • 具体实现类就是继承颜色接口的类:各种颜色

下面我们用代码看一下该如何设计。

代码实现

首先我们创建形状的基类Shape

//================== Shape.h ==================

@interface Shape : NSObject
{
    @protected Color *_color;
}

- (void)renderColor:(Color *)color;

- (void)show;

@end




//================== Shape.m ==================

@implementation Shape

- (void)renderColor:(Color *)color{

    _color = color;
}

- (void)show{
    NSLog(@"Show %@ with %@",[self class],[_color class]);
}

@end

由上面的代码可以看出:

  • 形状类Shape持有Color类的实例,二者是以组合的形式结合到一起的。而且Shape类定义了供外部传入Color实例的方法renderColor::在这个方法里面接收从外部传入的Color实例并保存起来。
  • 另外一个公共接口show实际上就是打印这个图形的名称及其所搭配的颜色,便于我们后续验证。

接着我们创建三种不同的图形类,它们都继承于Shape类:

正方形类:

//================== Square.h ==================

@interface Square : Shape

@end




//================== Square.m ==================

@implementation Square

- (void)show{

    [super show];
}

@end

长方形类:

//================== Rectangle.h ==================

@interface Rectangle : Shape

@end




//================== Rectangle.m ==================

@implementation Rectangle

- (void)show{

    [super show];
}

@end

圆形类:

//================== Circle.h ==================

@interface Circle : Shape

@end




//================== Circle.m ==================  

@implementation Circle

- (void)show{

    [super show];
}

@end

还记得上面的Shape类持有的Color类么?它就是所有颜色类的父类:

//================== Color.h ==================   

@interface Color : NSObject

@end




//================== Color.m ================== 

@implementation Color

@end

接着我们创建继承这个Color类的三个颜色类:

红色类:

//================== RedColor.h ==================

@interface RedColor : Color

@end




//================== RedColor.m ==================  

@implementation RedColor

@end

绿色类:

//================== GreenColor.h ==================

@interface GreenColor : Color

@end




//================== GreenColor.m ==================
@implementation GreenColor

@end

蓝色类:

//================== BlueColor.h ==================

@interface BlueColor : Color

@end




//================== BlueColor.m ==================

@implementation BlueColor

@end

OK,到现在所有的形状类和颜色类的相关类已经创建好了,我们看一下客户端是如何使用它们来组合成不同的带有颜色的形状的:

//================== client ==================


//create 3 shape instances
Rectangle *rect = [[Rectangle alloc] init];
Circle *circle = [[Circle alloc] init];
Square *square = [[Square alloc] init];

//create 3 color instances
RedColor *red = [[RedColor alloc] init];
GreenColor *green = [[GreenColor alloc] init];
BlueColor *blue = [[BlueColor alloc] init];

//rect & red color
[rect renderColor:red];
[rect show];

//rect & green color
[rect renderColor:green];
[rect show];


//circle & blue color
[circle renderColor:blue];
[circle show];

//circle & green color
[circle renderColor:green];
[circle show];



//square & blue color
[square renderColor:blue];
[square show];

//square & red color
[square renderColor:red];
[square show];

上面的代码里,我们先声明了所有的形状类和颜色类的实例,然后自由搭配,形成不同的形状+颜色的组合。

下面我们通过打印的结果来看一下组合的效果:

Show Rectangle with RedColor
Show Rectangle with GreenColor
Show Circle with BlueColor
Show Circle with GreenColor
Show Square with BlueColor
Show Square with RedColor

从打印的接口可以看出组合的结果是没问题的。

跟上面没有使用桥接模式的设计相比,使用桥接模式需要的类的总和是 m + n:当m或n的值很大的时候是远小于 m * n(没有使用桥接,而是使用继承的方式)的。

而且如果后面还要增加形状和颜色的话,使用桥接模式就可以很方便地将原有的形状和颜色和新的形状和颜色进行搭配了,新的类和旧的类互不干扰。

下面我们看一下上面代码所对应的类图:

代码对应的类图

桥接模式代码示例类图

从 UML 类图可以看出,该设计是由两个抽象层的类ShapeColor构建的,正因为依赖的双方都是抽象类(而不是具体的实现),而且二者是以组合的方式联系到一起的,所以扩展起来非常方便,互不干扰。这对于今后我们对代码的设计有比较好的借鉴意义。

优点

  • 扩展性好,符合开闭原则:将抽象与实现分离,让二者可以独立变化

缺点

  • 在设计之前,需要识别出两个独立变化的维度。

Objective-C & Java 的实践

  • Java:Spring-JDBC中的DriveManager通过registerDriver方法注册不同类型的驱动

原文发布于微信公众号 - 程序员维他命(J_Knight_)

原文发表时间:2019-03-27

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券