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

1.1概述

    将抽象部分与它的实现部分分离,使他们都可以独立地变化。这就是桥接模式的定义。

    抽象类或接口中可以定义若干个抽象方法,习惯上将抽象方法称作操作。抽象类或接口使程序的设计者忽略操作的细节,即不必考虑这些操作是如何实现的,当用户程序面向抽象类或接口时,就不会依赖具体的实现,使系统具有很好的扩展性。但是,抽象类中的抽象方法总归是需要子类去实现的,在大多数情况下抽象类的子类完全可以胜任这样的工作,但是在某些情况下,子类可能会遇到一些难以处理的问题。

  例如,电视台系统中有一个抽象类CCTV,该类有一个抽象方法abstract void makeProgram()。现在为了满足某些用户看电视剧的需求,这里给出了CCTV类的子类:CCTV8,该类的实例调用makeProgram()方法制作电视剧节目,因此子类CCTV8必须实现父类的makeProgram()方法,比如使用该方法制作出若干帧影像。子类CCTV8makeProgram()方法在制作出第一帧影像后,比如在第一帧影像显示“CCTV8”,马上就发现以下两个问题:

(1)从第2帧开始应当是电视剧中的影像,而这样的影像不应当由CCTV8类的makeProgram()方法负责制作。

(2)如果CCTV8makeProgram()方法中强行给出了第2帧以后的各个影像,那么用户使用CCTV8类的实例看到的电视剧是一个固定的电视剧,如果有其他用户想看新的电视剧,系统就必须新增新的CCTV子类,这对电视台系统是一个非常不合理的一种设计,因为CCTV类应当只有一个负责制作“电视剧”节目的子类:CCTV8,而不是多个,也就是说,不能因为一个新的用户要看不同的电视剧,就要出现一个专门为该用户制作“电视剧”节目的子类。

  针对上述问题,应当将实现和抽象放在两个不同的类层次中,从而使他们可独立的改变,即将一个抽象类中抽象方法的重要实现部分交给另外一个抽象类的子类或实现另外一个接口的类。比如,对于上述问题,应当将makeProgram()方法的实现交给另外一个抽象类:Program,该类定义了制作影像的makeTVfilm()方法。

  我们应当重新设计抽象类CCTV类,使该类包含Program的引用,这就可以使CCTV类的子类CCTV8在实现makeProgram()方法时,将该方法的重要实现部分交给Program类的makeTVfilm()方法,即委托给Program子类的实例调用makeTVfilm()方法。

  我们称CCTV类和Program类之间的关系是桥接关系,也即是说,CCTV类的子类CCTV8仅仅在CCTV类和Program类之间起到一个“桥接”的作用,具体类关系如下图一所示:

 

图一:电视节目与电视剧制作的桥接关系

 

 

1.2模式的结构

桥接模式包括以下四种角色:

(1)抽象(Abstration):是一个抽象类,该抽象类含有Implementor声明的变量,即维护一个Implementor类型对象。

(2)实现者(Implementor):实现者角色是一个接口(抽象类),该接口(抽象类)中的方法不一定与Abstration类中方法一致。Implementor接口(抽象类)负责定义基本操作,而Abstration类负责定义基于这些基本操作的较高层次的操作。

(3)细化抽象(Refined Abstration):细化抽象是抽象角色的一个子类,该子类在重写(覆盖)抽象角色中的抽象方法时,在给出一些必要的操作后,将委托所维护Implementor类型对象调用相应的方法。

(4)具体实现者(Concrete Implementor):具体实现者是实现(扩展)Implementor接口(抽象类)的类。

桥接模式结构的类图如下图二所示:

 

图二:桥接模式的类图

 

1.3桥接模式的优点

(1)桥接模式分离实现与抽象,使抽象和实现可以独立的扩展。当修改实现的代码时,不影响抽象的代码,反之也一样。

(2)满足开-闭原则。抽象和实现者处在同层次,使系统可独立地扩展者两个层次。增加新的具体实现者,不需要修改细化对象,反之增加新的细化对象也不需要修改具体实现。

 

1.4适合使用桥接模式的情景

1)不想让抽象和某些重要的实现代码是固定绑定关系,这部分实现可运行时动态决定。

2)抽象和实现者都可以继承的方法独立地扩充而互不影响,程序在运行期间可能需要动态的将一个抽象的子类的实例与一个实现者的子类的实例进行组合。

3)希望对实现者层次的代码的修改对抽象层不产生影响,即抽象层的代码不必重新编译,反之亦然。

 

 

1.5桥接模式的使用

以下通过一个简单的问题来描述怎样使用桥接模式,这个简单的问题是:计算建筑楼房的成本。

首先看一下本实例构建框架具体类和1.2模式的结构中类图的对应关系,如下图所示:

(1)抽象(Abstraction

本问题中,抽象角色是ArchitectureCose类,代码如下:

package com.liuzhen.sixteen_bridge;public abstract class ArchitectureCose {BuildingDesign design;double unitPrice;public abstract double giveCost();
}

 

(2)实现者(Implementor

对于本问题,实现者是BuildingDesign接口,BuildingDesign接口的代码如下:

package com.liuzhen.sixteen_bridge;public interface BuildingDesign {public double computerArea();
}

 

(3)细化抽象(Refined Abstraction

对于本问题,细化抽象角色是BuildingCose类,该类是ArchitectureCose类的子类。BuildingCose类在实现父类ArchitectureCose中的giveCose()方法时,需要根据建筑物的总面积和每平方米的造价给出整个建筑物的建造成本。但是,建筑物的建筑面积应当由建筑设计者提供,即建筑设计者负责计算建筑物的总面积,因此giveCose()的基本实现部分应当由一个和ArchitectureCose类同层次的BuildingDesign接口负责,即由实现BuildingDesign接口的类的实例负责面积的计算,而BuildingCose类负责定义基于这些基本操作的较高层次的操作,因此,BuildingCose类在实现giveCose()方法时将基于实现BuildingDesign接口的类的实例所给出建筑物面积来计算建造成本。BuildingCose类的代码如下:

package com.liuzhen.sixteen_bridge;public class BuildingCose extends ArchitectureCose{BuildingCose(BuildingDesign design, double unitPrice){this.design = design;this.unitPrice = unitPrice;}public double giveCost(){double area = design.computerArea();return area*unitPrice;}
}

 

(4)具体实现者(Concrete Implementor

对于本问题,具体实现者是HouseDesign类的实例,HouseDesign类的代码如下:

package com.liuzhen.sixteen_bridge;public class HouseDesign implements BuildingDesign{double width, length;int floorNumber;HouseDesign(double width, double length, int floorNumber){this.width = width;this.length = length;this.floorNumber = floorNumber;}public double computerArea(){return width*length*floorNumber;}
}

 

(5)具体使用

通过SixteenApplication类来具体实现上述相关类和接口,来实现桥接模式的运用,其代码如下:

package com.liuzhen.sixteen_bridge;public class SixteenApplication {public static void main(String[] args){double width = 63, height = 30;int floorNumber = 8;double unitPrice = 6867.38;BuildingDesign design = new HouseDesign(width, height, floorNumber);System.out.println("宽:"+width+"米,高:"+height+"米,层数为:"+floorNumber);ArchitectureCose cost = new BuildingCose(design,unitPrice);double price = cost.giveCost();System.out.println("每平方米造价:"+unitPrice+"元,商业楼的建设成本:"+price);}
}

 

运行结果:

宽:63.0米,高:30.0米,层数为:8
每平方米造价:6867.38元,商业楼的建设成本:1.0383478560000001E8

 

 

参考资料:

      1.Java设计模式/耿祥义,张跃平著.——北京:清华大学出版社,2009.5

转载于:https://www.cnblogs.com/liuzhen1995/p/6034311.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/372082.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux静默删除文件夹,Linux常用命令10 - unzip

zip 是最广泛使用的归档文件, 除了linux,windows也是非常的广泛。,支持无损数据压缩。 zip 文件是包含一个或多个压缩文件或目录的数据容器。接下来,我将解释如何使用 unzip 命令通过命令行解压缩 Linux 系统中的文件。 还有与之对应就是 zip…

python基础:迭代器、生成器(yield)详细解读

1. 迭代器 迭代器是访问集合元素的一种方式。迭代器对象从集合的第一个元素开始访问,知道所有的元素被访问完结束。迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退。 1.1 使用迭代器的优点 对于原生支持随机访问的数据…

Linux将硬盘转化为pv,Linux扩展硬盘 物理卷(PV) 卷组(VG) 逻辑卷(LV)

1、给虚拟机添加两块新的sata虚拟硬盘,容量8G和10G# fdisk -l 命令2、分别在这两个硬盘上建立pvPvcreate /dev/sdb 创建一个物理卷/dev/sdb 磁盘名是 fdisk -l 查询出来的Pvscan 查看当前所有物理卷Pvdisplay 查看当前所有物理卷的详情3、创建VG,使得…

ubuntu 16.10 shu rufa meiy ou l e geng xi zhi hou

转载于:https://www.cnblogs.com/ganmk--jy/p/6035894.html

红旗linux 进不去图形界面,进不了红旗Linux6.0的图形界面请高手帮忙

习生 于 2008-11-02 11:08:42发表:引用:原帖由 zhaoruiqi 于 2008-11-2 10:03 发表 我的也是进不了图形界面,用文本安装后进系统也一样正常按rtl的方法对xorg.conf进行修改,已经能进入图形界面。你看看楼上rtl的回复的能否对你有帮助。zhaoruiqi 于 2008-11-02 10:0…

基于Activiti工作流引擎实现的请假审核流程

概要 本文档介绍的是某商用中集成的Activiti工作流的部署及使用,该框架用的Activiti版本为5.19.0。本文档中主要以一个请假流程为例子进行说明,该例子的流程图如下: 这是一个可以正常运作的工作流业务了,但是它也有不足的地方&…

linux编译ffmpeg成so,「ffmpeg」一 mac 环境下编译ffmpeg,生成so库文件

1.下载ffmpeg源码,官网,我这里直接采用git 方式下载:下载ffmpeg.png终端输入git命令:静静等待~最后下载的版本为3.4.6 。image.png这里注意一下,刚开始我用的ndk版本是ndk-17b,在编译该版本的ffmpeg时始终失败&#xf…

Spring Security 3 Ajax登录–访问受保护的资源

我看过一些有关Spring Security 3 Ajax登录的博客,但是我找不到解决如何调用基于Ajax的登录的博客,匿名用户正在Ajax中访问受保护的资源。 问题 – Web应用程序允许匿名访问某些部分,并且某些部分是受保护的资源,需要用户登录。 …

linux运维选择题,初学Linux练习题

1、将/etc/issue文件中的内容转换为大写后保存至/tmp/issue.out文件中tr ‘a-z’ ‘A-Z’ < /etc/issue > /tmp/issue.out2、将当前系统登录用户的信息转换为大写后保存至/tmp/who.out文件中3、一个linux用户给root发邮件&#xff0c;要求邮件标题为”help”&#xff0c…

[转]Web Api系列教程第2季(OData篇)(二)——使用Web Api创建只读的OData服务

本文转自&#xff1a;http://www.cnblogs.com/fzrain/p/3923727.html 前言 很久没更新了&#xff0c;之前有很多事情&#xff0c;所以拖了很久&#xff0c;非常抱歉。好了&#xff0c;废话不多说&#xff0c;下面开始正题。本篇仍然使用上一季的的项目背景&#xff08;系列地址…

linux idea 快捷键,Linux 下 IDEA 的 Ctrl+Alt+S

前言这是个困扰我一年多的问题&#xff0c;今天终于解决了……起因一年前将主系统换成 Arch Linux 后&#xff0c;其他一切正常就是 IDEA 的打开设置的快捷键 ctrlalts 失效&#xff0c;让我很是头疼。虽然不是很重要&#xff0c;但是对于我这种强迫症来说别提多难受了……我曾…

C语言数字3转变字符 3 程序,大学c语言知识点总结

大学c语言知识点总结C语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。一起来看看大学c语言知识点总结吧!大学c语言知识点总结1、编译预处理不是C语言的一部分&#xff0c;不再运行时间。C语言编…

接触Jenkins(Hudson)API,第1部分

哪一个-哈德森还是詹金斯&#xff1f; 都。 几个月前&#xff0c;我开始使用Hudson v1.395来从事这个小项目&#xff0c;在出现巨大分歧之后又回到了这个项目。 我以此为契机&#xff0c;看我将来选择永久搬到詹金斯时是否会遇到任何重大问题。 有很多麻烦-最值得注意的是&…

使用javascript模拟常见数据结构(四)

七、树 树是一种非线性的分层的数据结构&#xff0c;在现实生活中比较常见的例子比如家谱和公司的组织架构图&#xff0c;如下所示&#xff1a; 一个树结构存在着一系列的父子结构&#xff0c;并且有着一个根节点&#xff0c;这种结构本质上表明了一对多的关系。 那&#xff0c…

最全Pycharm教程(10)——Pycharm调试器总篇

最全Pycharm教程&#xff08;1&#xff09;——定制外观 最全Pycharm教程&#xff08;2&#xff09;——代码风格 最全Pycharm教程&#xff08;3&#xff09;——代码的调试、执行 最全Pycharm教程&#xff08;4&#xff09;——有关Python解释器的相关配置 最全Pycharm教程&am…

Looper.prepare()和Looper.loop()

什么时候需要 Looper Looper用于封装了android线程中的消息循环&#xff0c;默认情况下一个线程是不存在消息循环&#xff08;message loop&#xff09;的&#xff0c;需要调用Looper.prepare()来给线程创建一个消息循环&#xff0c;调用Looper.loop()来使消息循环起作用&#…

如何查看Ubuntu版本,以及Linux内核版本??

查看Ubuntu版本&#xff1a; 方法一&#xff1a; cat /etc/issue 方法二&#xff1a; sudo lsb_release -a 查看内核版本&#xff1a; uname -r 转载于:https://www.cnblogs.com/tanrong/p/6937749.html

实现chrome扩展启动本地进程 - 补充

实现chrome扩展启动本地进程 - 补充 标签&#xff1a; chrome扩展启动本地程序访问本地磁盘2014-10-17 11:42 6753人阅读 评论(17) 收藏 举报分类&#xff1a;Chrome Plugin版权声明&#xff1a;本文为博主原创文章&#xff0c;未经博主允许不得转载。 示例 主要包含如下部分 c…

单路电压表c语言编程,用AT89C51单片机制作的数字电压表

此数字电压表&#xff0c;利用A/D转换原理将被测模拟量转换成数字量&#xff0c;并通过控制系统用数字方式显示测量结果。本设计采用AT89C51单片机&#xff0c;ADC0809进行模/数转换&#xff0c;能够测量8路0&#xff5e;5V的输入电压值&#xff0c;可用四位LED数码管轮流或单路…

ZK的实际应用:MVVM –加载和渲染数据

先前的文章简要介绍了RIA框架ZK&#xff0c;以及它CSS Selector启发式控制器机制如何通过使在控制器类中引用UI组件的任务变得相对灵活来减轻UI更改所带来的一些负担。 然后&#xff0c;我们在上一篇文章中探讨了ZK中的MVVM模式如何允许单个ViewModel提供不同的视图。 这篇文章…