专栏首页更流畅、简洁的软件开发方式再论桥接模式(上)纸上谈兵

再论桥接模式(上)纸上谈兵

声明: 1、 这里不是讲解桥接模式,因为我觉得我没有那个实力,我现在还没有完全理解桥接模式。 2、 这里只是想把我这几天的思考、在群里的讨论整理一下,给自己的学习道路上留下一个脚印 3、 因为前面写了一篇,现在看来有很多的问题,因为那时候并没有理解“抽象部分”,所以有很多的问题,现在的理解比那时侯又进了一步,所以需要在解释一下。 4、 我最怕的就是误导新人,误人子弟可是很大的罪过,所以我希望大家能够多多讨论,多多批评,哪怕我现在的理解还是错的,那也是新人一个警示,不要犯我这样的错误。

目的: 这一篇想弄明白下面几个问题: 1、 什么是抽象? 2、 抽象部分是什么? 3、 抽象部分是如何与实现部分分离的? 4、 抽象部分是如何独立变化的?(重点在于变化)

前言   我也是一直在不断的理解、消化各种知识,比如面向对象、设计模式等,每次小有收获的时候都想把成果写出来,于是我的博客里就有了一篇篇小小的博文。虽然写出来了,但是并不能保证就一定是正确的,写出来与人分享,与人讨论,检查自己的想法是否正确。只要有自己的想法就行,谁能保证自己的想法永远是正确的呢?在此感谢dudu为我们提供了一个与人交流讨论的平台!

  我们都是一步一步走过来的,可能当时觉得自己的想法是正确的,但是现在回过头来一看,当时的想法可能就是错误的。如果错了,那么我们改进原来的想法。于是我们在不断的进步。如果您觉得现在的想法和以前是一样的,那么说明您要么一直停滞不前,要么已经到达巅峰了,呵呵。

  前面写的那篇关于页面和桥接的确是写的不好,那时对桥接的理解还比较片面,现在又有了新的理解,所以我觉得有必要在说明一下,以免误导大家。

先出一个桥接模式的UML图。(来自TerryLee的http://www.cnblogs.com/Terrylee/archive/2006/02/24/336652.html

案例一:

  在网上找到了一个例子,我觉得很适合初步了解桥接模式。http://blog.csdn.net/abcdwxc/archive/2008/02/03/2079334.aspx 感兴趣的话,可以去看看,这里就不过多的引用哪里的描述了。

  他先说了一个不好的设计,蜡笔,如下图,这个就是一个排列,3种型号的蜡笔×12种颜色=36。这个数量就比较恐怖了,那么如何来减少这个数量呢?毛笔与颜料。一种毛笔可以用十二种颜色。颜色的变化,不用去考虑毛笔;毛笔的变化,也不用去考虑颜色。这个这个就是对桥接模式的第一步的理解吧。当初我的想法也是这样的,但是现在有个疑问。

  这么设计确实是把毛笔和颜色解耦了,也做到了“二者的独立变化”,但是毛笔和颜色谁是抽象部分,谁是实现部分呢?或者说抽象部分是谁? 原文的解释是“抽象层面的概念是:‘毛笔用颜料作画’”。这个好像没有什么错误,但是没有明确指出哪里是抽象部分,就是说UML图里,哪个地方是抽象部分?这个并没有明确的说明。

如果说毛笔和颜料都是抽象部分,那么毛笔的型号和各种颜色就是实现部分了。这就有两个疑问:

1、 他们解耦了吗?如果这样的话,那不就是说父类和子类是解耦的了。这显然是不对的。 2、 各种颜色独立变化了,毛笔型号也独立变化了,但是这都是实现部分的变化呀,抽象部分变化了吗?

看来这种划分抽象部分和实现部分的方法是不对的,应该“竖着”分,即左面的毛笔部分是抽象部分,右面的颜色是实现部分。但是这样也有疑问呀?

1、 左面的毛笔部分怎么就是抽象部分了,抽象在哪里?如果说毛笔是抽象的那还可以理解,但是大号毛笔就是具体的了,是独立的。 2、 毛笔并不依赖于颜色,他和颜色之间的关系并不密切,本来就是分开的,所以不用特意的去分离。 3、 毛笔并不需要用颜色来体现出来,就是说毛笔不需要用颜色来实现。

  这么分也是不对的,那么如何划分才是对的呢?其实毛笔和毛笔里的成员——颜色,和在一起才是抽象部分。现在实现了第二个目的,那么这个抽象部分又是如何与实现部分分离的?又是如何独立变化的呢?

  我觉得这个例子并没有体现出来,或者说是我没有理解出来。我们再换一个例子吧。

案例二:

  LoveBaoBao-DP 和我说了一下《设计模式解析2》里的桥接模式里的例子——画图。我没看过这本书,完全是根据他的讲述来理解的,因为经过了一个人的转述,所以我觉得我下面的UML图很可能和《设计模式解析2》里的是不一样的。

下面是 LoveBaoBao-DP 的描述:(案例二和案例一完全没有关系,请暂时忘记案例一)

画图
  图形 具体可以有,圆形,方形,菱形等,笔具体有,圆珠笔,钢笔,毛笔。那么这里有个问题,画图的时候,用钢笔画圆形,用毛笔画圆形,用毛笔画方形。这里笔和图形的子类具有耦合。简单说,图形是抽象,具体怎么画,是实现。也就是笔。如果,抽象出来图形,然后再给各种笔做个抽象父类,那么 就是两个抽象的依赖,图形的子类不用关心笔的子类了。这样达到了,抽象跟实现分离。
  延缓排列乘积,任意两端加一个子,不用关心因此而出现的那些组合种类,组合的总类是增加了,但是你不用去考虑,达到了延迟,没有桥接,出现一个子,就要写出所有组合的可能。

我按照自己的理解画了一个UML。 【UML】

(上面的是原来的UML图,根据的提问又修改了一下,感觉修改之后就更像了,呵呵。下面是修改后的。)

  在说这个“画图”之前我们先看看桥接模式的相关说明:(引自http://www.cnblogs.com/lds85930/articles/643166.html

桥梁模式的用意

  【GOF95】在提出桥梁模式的时候指出,桥梁模式的用意是"将抽象化(Abstraction)与实现化(Implementation)脱耦,使得二者可以独立地变化"。这句话有三个关键词,也就是抽象化、实现化和脱耦。

抽象化

  存在于多个实体中的共同的概念性联系,就是抽象化。作为一个过程,抽象化就是忽略一些信息,从而把不同的实体当做同样的实体对待【LISKOV94】。

实现化

  抽象化给出的具体实现,就是实现化。

脱耦

  所谓耦合,就是两个实体的行为的某种强关联。而将它们的强关联去掉,就是耦合的解脱,或称脱耦。在这里,脱耦是指将抽象化和实现化之间的耦合解脱开,或者说是将它们之间的强关联改换成弱关联。

将两个角色之间的继承关系改为聚合关系,就是将它们之间的强关联改换成为弱关联。因此,桥梁模式中的所谓脱耦,就是指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是继承关系,从而使两者可以相对独立地变化。这就是桥梁模式的用意。

含有两个等级结构,也就是:

  • 由抽象化角色和修正抽象化角色组成的抽象化等级结构。
  • 由实现化角色和两个具体实现化角色所组成的实现化等级结构。

桥梁模式所涉及的角色有:

  • 抽象化(Abstraction)角色:抽象化给出的定义,并保存一个对实现化对象的引用。
  • 修正抽象化(Refined Abstraction)角色:扩展抽象化角色,改变和修正父类对抽象化的定义。
  • 实现化(Implementor)角色:这个角色给出实现化角色的接口,但不给出具体的实现。必须指出的是,这个接口不一定和抽象化角色的接口定义相同,实际上,这两个接口可以非常不一样。实现化角色应当只给出底层操作,而抽象化角色应当只给出基于底层操作的更高一层的操作。
  • 具体实现化(Concrete Implementor)角色:这个角色给出实现化角色接口的具体实现。

 ===================引用完毕============================

对照这个说明,来看看“画图”的情况

=========================原先的想法=========================

两个等级结构: 抽象化等级结构:图形。 实现化等级结构:用画笔画图。

抽象化角色:图形、画笔。 修正抽象化角色:各种图形,比如圆形,长方形,正方形,菱形等。 实现化角色:(用画笔)画图形。具体一点就是画出一个“点”。(当初学几何的时候老师教导我们:点动成线)。 具体实现化角色:各种画笔风格的实现,就是实现用铅笔画一个点的效果、用毛笔画出一个点的效果等等。

=========================修改后的想法=========================

两个等级结构:

抽象化等级结构:图形。 实现化等级结构:用画笔画图形。

抽象化角色:图形。 修正抽象化角色:各种图形,比如圆形,长方形,正方形,菱形等。 实现化角色:(用画笔)画图形。具体一点就是画出一个“点”。(当初学几何的时候老师教导我们:点动成线)。 具体实现化角色:各种画笔风格的实现,就是实现用铅笔画一个点的效果、用毛笔画出一个点的效果等等。

图形和笔的关系   图形是一个抽象概念,长方形也是一个抽象概念。假设我不知道什么是长方形,那么您要解释的话,恐怕要费不少口舌,那么最直接的办法就是,用笔画出来一个长方形,这样就有了一个感性的认识。

  长方形通过笔实现(画)了出来。图形想要实现出来就要通过笔,但是图形有好多种分类,笔也有很多种类。如果参合到一起,那就累了,所以需要分离开来,独立变化。

  图形要想实现出来,就得依赖笔,这就是一种强耦合,那么想办法把这种偶合解脱开,那么就是所谓的解耦了。

  抽象部分是什么?图形,实现部分是什么?把图形画出来。这里有点绕,也是桥接模式难于理解的地方。画图是由右面的笔来负责实现的,但是怎么画却是左面的图形来规定的。这个本来是分不开的,但是桥接模式就是要把他们给分离开。有难度才有挑战,才有必要创建一个模式来解决。 那么是如何分开的呢——组合。在图形里面定义一个笔的成员,然后在子类里面通过这个成员与笔进行合作。

  我的理解就是,用笔画图形就是抽象,而实现部分有两种不同的分类,一个是笔的分类:铅笔、毛笔、喷枪等;一个是图形的分类:直线、圆、长方形等。他们可以各自独立的变化,互补干扰。笔在扩展的时候,不需要考虑有多少种图形;图形在扩展的时候也不需要考虑是铅笔还是毛笔。

  问题似乎都解决的,但是对于左面的“图形”部分还有疑问,在《深入浅出设计模式》里面,桥接模式是用遥控器和电视机来举例子的,遥控器是抽象的,电视机是实现部分。思路应该是“遥控器控制电视转换频道,遥控器只是控制(下命令,没有实现),而具体的换频道的任务由电视机实现”。但是遥控器只画了一个子类,而且子类和父类里的函数名称有不尽相同。《深》并没有给出代码,所以这个我还没有理解透彻。

  不过想一想,可以“变化”就一定要用多个子类的方式实现吗?如果是这样的话,那么我的UML图是不是要改一下呢?

========================================

ps:

  标题为什么要用“纸上谈兵”呢?纸上谈兵就是说,理论上一套一套的,但是到了实际中就不行了。

  因为在我找到的例子中,有一部分例子是为了说明桥接模式而“构想”出来的,并不是真实的项目。您可以通过这些例子去了解、体会桥接模式,但是千万不要以为真实的代码就是这样的。所以我用了纸上谈兵这个成语。

  但是我并不是说这么做不对,而只是想说,看了这样的代码,可以学会桥接模式,但是到了实际中很可能还是不会用。最好的方法就是给出真实项目里的例子。但是这个难度确实很大,一到了真实项目里,就会遇到很多细节问题,从而影响对模式的理解,而且真实项目都带有一定的业务环境,首先要了解这个业务环境才能理解,这就又增加了难度。

  最后,解决一个问题往往有很多种方法,一提到具体问题,您可能就马上想到了另一种更好的解决方法,就会觉得他说的不好,从而就会阻碍对讲解的理解。

  不过我还是想尝试一下用真实的例子来解释一下桥接模式。下一篇就会详细说明。这里没写代码,我对伪代码不太擅长,下一篇会写真实的代码的。

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

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 增删改查不是万能的,但是万万不能没有增删改查——限信息管理类

      感谢大家对我的支持,上一篇(【角色】——分离开代码和权限需求,即实现代码和权限需求的解耦。 )的推荐数达到了37 。这是大家对我的认同、鼓励、支持、和期望。...

    用户1174620
  • Step By Step 一步一步写网站[1] —— 填加数据

    填加数据是一个项目必不可少的部分,也是一个基础操作,使用也是最频繁的。 那么您是怎么实现添加数据的呢? 添加数据可以分为几种情况。 1、单表添加,不需要事务。...

    用户1174620
  • ajax的再次封装!(改进版) —— new与不 new 有啥区别?

      生命不息重构不止!   上一篇写了一下我对ajax的再次封装的方法,收到了很多有价值的回复,比如有童鞋建议用$.extend,一开始还以为要做成插件呢,后来...

    用户1174620
  • 全面解析YOLO V4网络结构

    作者|周威,https://zhuanlan.zhihu.com/p/150127712

    AI算法修炼营
  • web开发快餐式入门指南 1.1 http

    由于web应用大多数都在浏览器中进行操作,所以我们有必要先了解一下浏览器里面到底发生了什么。简而言之,当你在浏览器的地址栏中输入网址并按下回车,或者点击了网页上...

    ApacheCN_飞龙
  • 零基础入门深度学习 | 第三章:神经网络和反向传播算法

    无论即将到来的是大数据时代还是人工智能时代,亦或是传统行业使用人工智能在云上处理大数据的时代,作为一个有理想有追求的程序员,不懂深度学习这个超热的技术,会不会感...

    用户1332428
  • KDD 2018 的首个「深度学习日」,要让数据挖掘会议更「纯粹」

    AI 科技评论按:深度学习近年来对数据科学产生了革命性的影响。基于计算能力的提升、数据来源的延展及编程框架的进步,深度神经网络已经无处不在。目前深度学习的相关方...

    AI科技评论
  • Linux云服务器下Redis安装与部署

    这里不建议先解压再上传到服务器,之前我这样做,编译报错,后来上传压缩包并按照官方步骤解压编译正常

    二十三年蝉
  • 没看完这11条,别说你精通 Python 装饰器

    对于每一个学习 Python 的同学,想必对 @ 符号一定不陌生了,正如你所知, @ 符号是装饰器的语法糖,@符号后面的函数就是我们本文的主角:装饰器。

    猴哥yuri
  • 本次更新包括集成CDSW1.3

    目前互联网大量web的应用层协议从http迁移到了https,https已经在越来越多的场合替换http协议。近期由于业务需要,我们通过Wireshark对ht...

    用户3138296

扫码关注云+社区

领取腾讯云代金券