相信大家都玩过「俄罗斯方块」吧。

小罗年幼时最喜欢玩的就是俄罗斯方块。作为一个有情怀的程序员,小罗决定尝试实现这款游戏。

玩过俄罗斯方块的人都会知道,俄罗斯方块由七种简单形状组成:

  • I、J、L、O、S、T、Z

小罗了然于心,抄起手中的键盘就创建了七个类。

黑色过于单调,所以小罗又选了三种颜色准备为这些方块着色:

  • Yellow
  • Blue
  • Red

要实现这样的需求,最 low 的方法就是为每种形状创造所有颜色的版本

如果采用这种方案,双方之间处于强链接,类之间关联性极强,如要进行扩展,必然导致类结构急剧膨胀:

如果仅用继承实现,我们会创造至少 3 * 7 = 21 个类。

当我们想增加 1 种形状(或颜色)的时候,就需要新增 3 (或 7)个类。

数量爆炸的类 == 差劲的扩展能力 == 爆炸的维护成本

从 SOLID 原则来看,以上设计违背了「开放 - 封闭原则」。已知的,在设计类继承的时候,良好的设计应该是保持引起类变化的因素只有一个,也就是所谓的「单一职责原则」。

那有没有环保一点的方式呢?让我们来看看「桥接模式」是怎么解决的。

概念

桥接模式的定义比较简洁:

把事物对象和其具体行为、具体特征分离开来,使它们可以各自独立的变化。 —— wikipedia

换言之,即 抽象化与实现化解耦,使得二者可以独立变化

根据 GOF 提到的,桥接模式由四部分组成:

  1. 抽象类:定义了一个实现类接口类型的对象并可以维护该对象。
  2. 扩充抽象类:扩充由抽象类定义的接口,它实现了在抽象类中定义的抽象业务方法,在扩充抽象类中可以调用在实现类接口中定义的业务方法。
  3. 实现类接口:定义了实现类的接口,实现类接口仅提供基本操作,而抽象类定义的接口可能会做更多更复杂的操作。
  4. 具体实现类:实现了实现类接口并且具体实现它,在不同的具体实现类中提供基本操作的不同实现,在程序运行时,具体实现类对象将替换其父类对象,提供给客户端具体的业务操作方法。

Java 实现

在使用桥接模式时,我们首先应该识别出一个类所具有的两个独立变化的维度,将它们设计为两个独立的继承等级结构,为两个维度都提供抽象层,并建立抽象耦合。

即,我们需要根据实际需求对形状和颜色进行组合。

既然是组合,接口肯定是少不了的,先创建颜色接口(这里也称作「桥接口」):

public interface Color {public void drawShape(String type);
}
复制代码

以及各种颜色类:

public class Red implements Color {@Overridepublic void drawShape(String type) {System.out.println("Red " + type +" is drawn");}
}public class Yellow implements Color {@Overridepublic void drawShape(String type) {System.out.println("Yellow " + type +" is drawn");}
}public class Blue implements Color {@Overridepublic void drawShape(String type) {System.out.println("Blue " + type +" is drawn");}
}
复制代码

然后,我们创建最重要的形状抽象类:

import Bridge.Java.Color.Color;public abstract class Shape {Color color;public void setColor(Bridge.Java.Color.Color color) {this.color = color;}public abstract void draw();
}
复制代码

同样创建具体的方块:

public class ShapeI extends Shape {@Overridepublic void draw() {color.drawShape("ShapeI");}
}
复制代码

测试:

Color red = new Red();
Shape shapeI = new ShapeI();
// 红色的 I
shapeI.setColor(red);
shapeI.draw();// 红色的 L
Shape shapeL = new ShapeJ();
shapeL.setColor(red);
shapeL.draw();
复制代码

以上,我们将「形状」和「颜色」解耦。

Hint: 如果你依旧有所疑惑,请回顾最开始的定义:

把事物对象和其具体行为、具体特征分离开来,使它们可以各自独立的变化。

此时,如需添加新的颜色或形状,我们只用实现一个桥接口或者继承一个抽象类即可。

优缺点

以上,相信你对桥接模式已经有所了解。

再我们来看看它的优缺点。

优点

  1. 抽象和实现的分离。
  2. 优秀的扩展能力。
  3. 实现细节对客户透明。

缺点 桥接模式需要建立在你对系统充分的认知下,需要我们识别出两个合理的变化维度,所以适用范围受到限制。

所以你什么时候该使用桥接模式呢?

适用场景

  1. 正如我们上方的例子,如果一个场景存在两个独立变化的维度,且这两个维度需要频繁扩展或变动时,我们优先考虑桥接模式。

  2. 如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。

  3. 对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。

  4. 其他。

Scala 实现

在 Scala 中,桥接模式的实现与 Java 大同小异,我们只需将接口关键字改为 trait

颜色接口:

trait Color {def drawShape(`type`: String)
}
复制代码

颜色类:

class Red extends Color{override def drawShape(`type`: String) = println(s"Red ${`type`} is drawn")
}class Blue extends Color{override def drawShape(`type`: String) = println(s"Blue ${`type`} is drawn")
}class Yellow extends Color{override def drawShape(`type`: String) = println(s"Yellow ${`type`} is drawn")
}
复制代码

形状抽象类以及实现类:

abstract class Shape(color: Color) {def draw()
}class ShapeI(color: Color) extends Shape(color){override def draw(): Unit = color.drawShape("ShapeI")
}class ShapeJ(color: Color) extends Shape(color){override def draw(): Unit = color.drawShape("ShapeJ")
}....
复制代码

也许部分同学会问:这里抽象类可以用 trait 代替吗?trait扩展性会不会更好?具体还是参考这里吧:abstract class 比 trait 好在哪里?

测试:

object Test extends App{new ShapeI(new Blue).draw()new ShapeJ(new Red).draw()
}
复制代码

总结

桥接模式用一种巧妙的方式处理多层继承存在的问题,用抽象关联取代了传统的多层继承,将类之间的静态继承关系转换为动态的对象组合关系,使得系统更加灵活,并易于扩展,同时有效控制了系统中类的个数。在系统设计初期,合理利用桥接模式,会让系统更加优雅。

源码链接 如有错误和讲述不恰当的地方还请指出,不胜感激!

Scala 与设计模式(六):Bridge 桥接模式相关推荐

  1. Java 设计模式之Bridge桥接模式

    将抽象和具体实现两个维度分离,让他们独立发展,并且在抽象类里聚合一个具体实现,通过聚合替代继承,用于解决类爆炸的问题. 这种模式要求对系统的抽象的理解和设计难度都比较大. 例子: 这里要对花,气球等装 ...

  2. C++设计模式-Bridge桥接模式

    作用:将抽象部份与它的实现部份分离,使它们都可以独立地变化. 将抽象(Abstraction)与实现(Implementation)分离,使得二者可以独立地变化. 桥接模式号称设计模式中最难理解的模式 ...

  3. python的编程模式-实例解析Python设计模式编程之桥接模式的运用

    这篇文章主要介绍了Python设计模式编程之桥接模式的运用,桥接模式主张把抽象部分与它的实现部分分离,需要的朋友可以参考下 我们先来看一个例子: #encoding=utf-8 # #by panda ...

  4. 简易理解设计模式之:桥接模式——穿衣服经典案例2

    介绍: 桥接模式属于结构型模式.它的定义为:将抽象部分与实现部分分离,使它们都可以独立的变化. 类图: Abstraction(抽象化角色):抽象部分,保持对实现部分对象的引用,抽象部分中的方法需要调 ...

  5. 步步为营 .NET 设计模式学习笔记 十三、Bridge (桥接模式)

    概述 在软件系统中,某些类型由于自身的逻辑,它具有两个或多个维度的变化,那么如何应对这种"多维度的变化"?如何利用面向对象的技术来使得该类型能够轻松的沿着多个方向进行变化,而又不引 ...

  6. Net设计模式实例之桥接模式( Bridge Pattern)(4)

    3.客户端代码<?XML:NAMESPACE PREFIX = O /> static void <?XML:NAMESPACE PREFIX = ST2 />Main(str ...

  7. 【设计模式自习室】桥接模式 Bridge Pattern:处理多维度变化

    前言 <设计模式自习室>系列,顾名思义,本系列文章带你温习常见的设计模式.主要内容有: 该模式的介绍,包括: 引子.意图(大白话解释) 类图.时序图(理论规范) 该模式的代码示例:熟悉该模 ...

  8. 设计模式学习笔记(十六:桥接模式)

    1.1概述 将抽象部分与它的实现部分分离,使他们都可以独立地变化.这就是桥接模式的定义. 抽象类或接口中可以定义若干个抽象方法,习惯上将抽象方法称作操作.抽象类或接口使程序的设计者忽略操作的细节,即不 ...

  9. 设计模式(八):Bridge桥接模式 -- 结构型模式

    1. 概述 在软件系统中,某些类型由于自身的逻辑,它具有两个或多个维度的变化,那么如何应对这种"多维度的变化"?如何利用面向对象的技术来使得该类型能够轻松的沿着多个方向进行变化,而 ...

最新文章

  1. Linux综合大作业
  2. 批量更新日期字段中的年
  3. VIM不正常退出产生的swp文件
  4. 【渝粤教育】 国家开放大学2020年春季 3956★汽车故障诊断技术 参考试题
  5. 【原】Win SQL Server2012 IIS 安装(图文详解)
  6. 腾讯优图发布四大平台产品,持续开放视觉AI能力
  7. Spring bean 不被 GC 的真正原因
  8. oracle未获得监听器,无监听文件listener.ora的动态监听小例试验
  9. 双十一 马云的淘宝如何通过数据分析得到利润?
  10. linux鼠标移动消失,debian6升级后鼠标指针消失
  11. 判断目录下的文件或者文件夹是否存在
  12. Spring Boot REST 请求(GetMapping,PostMapping)、格式化日期参数、压缩响应内容
  13. 程序员的恶性循环:加班-没空学习-老是写同等水平代码-无法提升代码质量-老是出BUG-老是需要修改-加班-......
  14. 如何学习嵌入式Linux_韦东山
  15. 下载列表失败_Mac配置的Motrix,高速下载百度云
  16. python字典函数values(),keys(),items()的用法与区别
  17. 【EXLIBRIS】#小词旮旯# 004 Camera
  18. java crx,如何以编程方式创建chrome crx文件(最好在Java中)?
  19. 前端项目实战11-hook usememo使用
  20. MapReduce中文版论文

热门文章

  1. visual stdio 工程 宏
  2. JMeter中如何实现参数化(CSV)
  3. udt编写高性能服务器,基于UDT协议的Oracle数据库远程备份的设计和实现
  4. 多个 小程序_最简单的小程序制作方法,不会代码也能撸一个
  5. python的回收机制_Python的垃圾回收机制深入分析
  6. kesioncms ajax分页,改进KesionCMS V9.0x SQL标签分页支持嵌套
  7. ajax servlet怎么接收_【百战程序员从开始到植发】之AJAX
  8. java进程生产者消费者_生产者与消费者(多线程经典案例)
  9. CocosCreator发布web-desktop获取本地资源
  10. 绝对干货!纯用HTML+CSS+JS 编写的计算器应用