对Bridge模式的理解

6、深入理解BRIDGE设计模式 本文深入探讨了BRIDGE设计模式的核心思想及其在实际项目中的应用方法。通过将抽象与其实现分离,BRIDGE模式提高了系统的灵活性和可扩展性,适用于控制设备代码、数据库驱动程序及图像代理等场景。文章还讨论了模式的优缺点,并提供了优化建议,帮助开发者更好地应对复杂软件设计挑战。 阅读详情

对Bridge模式的理解

摘要:本文首先解释了Bridge模式的定义。然后通过一个例子,一步步将Bridge模式实现。

 

在一切开始之前,请允许我先给出三条经典名言:Design to interfaces. Find what varies and encapsulate it. Favor composition over inheritance.后面我们会反复,并且是反反复复的用到。我认为在做设计的时候这三句话要牢牢的印在脑子里。

一.  定义

根据GOF的定义,Bridge模式的目的是“解耦抽象与它的实现,以便二者可以独立的变化。”这个定义中最容易误解的“抽象”与“它的实现”。因为这两个词在面向对象的语言中都有对应的关键字。在Java中即是“abstract”和“implement”,所以很容易造成困惑的是认为要解耦一个抽象类和它的实现类。实际上,这里实现是指的抽象类和它的派生类用以实现自己的对象,进一步说就是这里的抽象指的是一个概念或者说是一个继承体系中的对象,而实现被抽象使用并完成自己的功能。举个例子,这是吕震宇兄想出的一个例子(http://www.cnblogs.com/zhenyulu/articles/67016.html),这是我见过的最经典的例子了,以至于我不得不在这里重复一遍:

小时候我们都用蜡笔画画,一盒蜡笔12种颜色。一开始我都是用最小号的蜡笔画个太阳公公、月亮婆婆足够了。后来开始画一些抽象派的作品,就得换中号的了,要不然画个背景都要描半天,好一盒中号的也是12种颜色。再后来我开始转向豪放派,中号就有些捉襟见肘了,只好换大号的了,好一盒大号的也只有12种颜色。你看,像我这样不太出名的画家就需要36种画笔,哇,太麻烦了。但是据我观察,另一些比我出名的画家倒是没有这么多笔,他们只有几把刷子和一些颜料,这样就解决了蜡笔的“种类爆炸”问题。如下图所示,注意图也是从吕兄的网站偷来的。


我要用
36种蜡笔

齐白石老先生只用3种毛笔和12种颜料

回到上面的定义,定义里面的抽象指的就是毛笔,实现就是颜料。颜料被毛笔使用,用以完成毛笔的功能。


(
http://www.niufish.com/books/Pattern/com/niufish/pattern/bridge/package-use.html)


在下认为上图中有以下几点可以变通:

1.   Abstraction与Implementor的聚合关系。该关系可以由RefinedAbstraction和Implementor的聚合关系代替。理由是Java编程的时候Abstraction可能会实现为interface,不能与Implementor构成聚合关系。

2.   Operation方法不一定在Abstraction中做实现,理由同上,但是必须声明。

可见在Bridge模式中有两个继承体系,为了方便描述我们称左边的为Abstraction继承体系,右边为Implementor继承体系。Abstraction使用Implementor完成自己的功能。同时,该模式允许Abstraction和Implementor各自独立变化(所谓变化,我认为就是派生)。

二.  解决的问题

上面已经说了,提出毛笔的概念目的是解决蜡笔的“种类爆炸”问题。3×12变成了3+12,并且在将来毛笔型号和颜料种类可以独立的扩充。上面的例子在说明这种设计模式的特点和优势上很有好处,但是毕竟我们实际编码中很少这么幸运的碰上这么简单的问题。下面我用一个相对复杂的问题来重新描述这个模式,关于蜡笔和毛笔的故事的代码可以到上面给出的链接查找。

三.  一个更加复杂的例子

这里我们给出一个更为复杂的例子,并且用一种循序渐进的方式描述,逐渐加入新的功能和约束条件。设想我们要做一个编辑器(Editor),可以打开文本文件,但是不同的文件要求用不同的编辑器打开。比如“.txt”文件用文本编辑器打开,而“.xml”文件用xml编辑器打开。


代码如下:

Editor接口

package com.gemplus.editor;

public abstract interface Editor {

    public void openFile(String path); 

}

TextEditor实现

package com.gemplus.editor; 
public class TextEditor implements Editor {
    public void openFile(String path) {
        System.out.println("Open file with Text Editor. FileName: " + path);
    }
}

XMLEditor实现

package com.gemplus.editor; 
public class XMLEditor implements Editor { 
    public void openFile(String path) {
        System.out.println("Open file with XML Editor. FileName: " + path);
    }
}

 Client

package com.gemplus.editor; 
public class Client {
    public static void main(String[] args) {
        Editor xmlEditor = new XMLEditor();
        xmlEditor.openFile("test.xml"); 
        Editor textEditor = new TextEditor();
        textEditor.openFile("test.txt");
    }
}

 

输出

Open file with XMLEditor. FileName: test.xml

Open file with Text Editor. FileName: test.txt

到目前为止,我们什么模式也没用到。不过我们还是用到了一项伟大的技术多态,还有就是对接口编程。代码写的还算优雅,只是没有写注释。

接下来我们要增加一个功能,与其说是功能,不如说是一项约束。就是,我不希望客户端了解那些文件要由xmlEditor打开,哪些由textEditor打开。但是总要有人知道,对吧?这项看似直观的约束往往被忽略,或者在潜意识中没有意识到。为了简单起见,我们引入一个简单工厂EditorImpl。

 

public class SmartEditor implements Editor {
 
    private static Editor textEditor = new TextEditor();
    private static Editor xmlEditor = new XMLEditor();
    
    public void openFile(String path) {
        if (path.endsWith(".xml")) {
            xmlEditor.openFile(path);
        } else {
            textEditor.openFile(path);
        }
    }
}

Client也要做相应修改

public class Client {
    public static void main(String[] args) {
//        Editor xmlEditor = new XMLEditor();
//        xmlEditor.openFile("FileName");
//        Editor textEditor = new TextEditor();
//        textEditor.openFile("FileName2");
        Editor editor = new SmartEditor();
        editor.openFile("test.xml");
        editor.openFile("test.txt");
    }
}

输出结果与修改之前完全一样,但是现在Client端已经不需要知道应该使用哪个Editor了,这其实也是对Find what vary and encapsulate it的应用。另外一点,也可以请大家注意在EditorImpl的实现中,即继承了Editor接口又使用了组合这其实是联合inheritance和composition的优点。Javaworld上有一篇文章(作者Bill Venners)就是讲这个技巧和使用方法的,我找了半天没找到,以后找到再把链接添上。

先别高兴太早,我们看看目前这种设计有什么问题。SmartEditor在充满了技巧和高级的、伟大的技术的同时,你有没有觉得它管的东西太多了呢?一个Editor要去关心文件名的问题,不错SmartEditor出现的目的就是要来关心文件名,然而从概念上讲,仍然不是好的设计。我们来看看什么在变化?答案是文件类型。OK,封装之!

我们把它称作什么呢?对应与Edtior,我们不妨称之为Editable。(说实话,我这样这个概念建立起来有一些生硬,但是你可以想像一下:如果被编辑的东西是更加复杂的呢?我所取的例子实际上是我工作中的一个例子,要编辑的东西要比这里描述的复杂的多!)


简单之极,以至于我觉得没有必要给出代码。但是第一次写文章,总要给大家留点好印象。

public class FileEditable implements Editable {
    Editor editor;
    String filePath;
 
    public FileEditable(Editor editor, String filePath) {
        this.editor = editor;
        this.filePath = filePath;
    }
 
    public void open() {
        editor.openFile(filePath);
    }
 
}

Client端

public class Client {
    public static void main(String[] args) {
 
        Editable editableText = new FileEditable(new TextEditor(), "test.txt");
        Editable editableXML = new FileEditable(new XMLEditor(), "test.xml");
        editableText.open();
        editableXML.open();
    }
}
输出仍然没有变化。(前面做的其实也可以算作是重构吧,可观察行为没有变化)
不得不承认,现在客户端又需要了解什么样的文件用什么编辑器打开了。但是一个小的变化就可以避免这一点。为FileEditable增加一个不包含Editor参数的构造函数。
    public FileEditable(String filePath) {
        this.filePath = filePath;
        if (filePath.endsWith(".xml")) {
            editor = new XMLEditor();
        } else {
            editor = new TextEditor();
        }
    }
客户端,这里就不给出了。

现在该是增加功能的时候了。现在我们要求,用户不但可以打开文件也有可能用这个Editor来编辑一段文字,即字符串。我们假设,所有的Editor都可以编辑文件和字符串。我们为Editor增加一个方法:openString。前面我们说了,Bridge模式中有两个继承体系,两边可以独立变化。现在我们有了两个Editable,即两种可编辑体:文件和字符串。也可以看出我们当初把Editable封装起来多么的英明神武啊!!

在Editable的继承体系中又增加了一员:StringEditable。


虽然我前面已经说了,
Abstraction和Implementor的聚合关系可以由派生类RefinedAbstraction和Implementor的聚合关系代替,为了与经典模式类图保持尽量的相似,以便大家容易理解,我们还是在StringEditable和FileEditable上面加了一层:EditableImpl。


好了,我们已经实现了
Bridge模式。不过这里面还是有一点让人不舒服的地方,就是Editor中包含的两个方法,实际上对应了两种Editable。也许是这个例子举的不好。但是《设计模式 explained》中的例子和本例大同小异。

我也是刚刚开始学习设计模式,如果有什么不对的地方请大家指出。接下来,我会继续这个例子,增加一个更加复杂的功能:可以嵌套组合的Editor。比如对于同一个文件我们可能希望有两种打开方式,以网页为例,我们希望在一个Editor中包含两个子Editor,分别显式源代码和页面效果。这里面会用到Composite模式。

参考资料:

1.   http://www.cnblogs.com/zhenyulu/articles/67016.html

《设计模式随笔-蜡笔与毛笔的故事》本文对于Bridge模式给了一个相当漂亮的比喻,并且给出了代码示例。

2.   http://www.cnblogs.com/zhenyulu/articles/62720.html

《设计模式(16)-Bridge Pattern》这篇文章没有仔细看,前面的角色定义和解释很精辟。

3.   http://www.cnblogs.com/idior/articles/97283.html

《Bridge Strategy 和State的区别》个人认为,作者在Bridge和Strategy模式的区别上面有误解。下面有我的回复:

我倒是不赞同楼主所说在“蜡笔与毛笔的故事”中“缺少被抽象的行为”的说法。Bridge模式中解耦的是“抽象”与“实现”。这里的实现不一定是对“行为”的抽象,按照《Design Pattern Explained》一书中所说,这里实现是指的抽象类和它的派生类用以实现自己的某个对象(本身是抽象与其派生类构成的继承体系,要不怎么变化呢)。进一步说就是这里的抽象指的是一个概念或者说是一个继承体系中的对象,而实现则是被抽象使用并完成自己的功能的另一个继承体系。桥梁模式的目的就是让这两个继承体系可以,独立的变化、派生。蜡笔与毛笔的故事中毛笔(第一个继承体系)和颜料(第二个继承体系)可以独立的变化,所以我认为这是个非常恰当的桥梁模式的例子。

继续:)

我觉得区分两个模式的方法不是从模式的实现上面看,因为一个模式的实现往往夹杂了其它的模式,比如idior给的第一个例子中就有Template Method模式。我赞成吕震宇的说法,这里面有Bridge模式的影子,甚至我觉得不止是影子,这本身是一个Bridge模式的例子。

区分两个模式的方法应该从解决的问题上看,也就是从context上分析。

我觉得简单的说Strategy模式是从N变化为1+N,原来有N个类但是这N个类里面只有某个算法的区别,我们把N个算法提取出来就变成了1个抽象类(不要理解成Java中的abstract class,而是这个抽象类表示一个概念)和N个实现类(同理,不要理解成对前面那个抽象类的实现,而是辅助实现抽象类的某个功能的一个继承体系)。注意这里只有一个继承体系。

而Bridge模式是从M×N变化为M+N,原来系统中有M×N个类,但是从中可以提取出N个算法(或者辅助类)和M个主体(我想不出一个好的名次)。这样构成了两个继承体系,N个算法(颜料)构成一个继承体系,M个主体类(毛笔的不同型号)构成一个继承体系。两个继承体系可以独立的变化。

从解决的问题上看,二者都要解决重复代码的问题,但是前者不强调锥把(见吕震宇的回复)的变化,而后者强调,并且强调锥头和所有锥把的兼容。我认为这才是二者的根本区别。

这是我个人的理解,与楼主商榷。

4.    http://www.niufish.com/books/Pattern/com/niufish/pattern/bridge/package-use.html

设计模式速查,很不错

什么是PCIe Relaxed Ordering 什么是PCIe上的Relaxed Ordering呢?直接翻译的话就是宽松排序。在理解这个东西之前,需要先了解下什么是PCIe的基本事务排序法则。 阅读详情

相关推荐

避坑指南:Win10安装Docker Desktop时Hyper-V报错的7种解决方案(2024最新版)

本文提供了2024年最新、最全面的Windows 10系统下Docker Desktop安装时Hyper-V报错的解决方案指南。文章深入剖析了Hyper-V与Docker的共生关系,并系统性地给出了从BIOS/UEFI设置、Windows功能启用、bcdedit命令修复到处理多虚拟化环境共存等7种核心解决方案,帮助开发者彻底解决安装困局,顺利部署容器化开发环境。

weixin_28840143的博客 529

【设计模式——学习笔记】23种设计模式——桥接模式Bridge(原理讲解+应用场景介绍+案例介绍+Java代码实现)

讲解桥接模式的思想,介绍其常见的应用场景,举例说明桥接模式的实现方式

我只管埋头前行,剩下的交给时间。 3290

Windows下用RKDevTool解包update.img的完整流程(含常见错误排查)

本文详细介绍了在Windows系统下使用RKDevTool工具解包update.img固件的完整流程。从工具准备、环境搭建到逐步解包操作,重点解析了输出文件结构,并提供了修改分区后重新打包的实战方法。文章还针对解包失败、打包错误等常见问题提供了详细的排查与解决方案,旨在帮助嵌入式开发者和爱好者高效完成固件分析与定制工作。

aa123的博客 715

桥接模式(Bridge)

桥接(Bridge)模式

weixin_43956958的博客 2万+

HALCON卡尺模型实战:5分钟搞定工件尺寸测量(附完整代码)

本文详细介绍了HALCON卡尺模型在工件尺寸测量中的实战应用,通过5分钟快速搭建测量系统的完整指南,包括基础配置、高级参数调优和完整代码示例。卡尺模型能自动识别边缘并计算精确尺寸,显著提升工业视觉检测的效率和一致性,特别适用于实时测量场景。

weixin_42570296的博客 462

当爬虫遇到TikTok滑块:一份给开发者的verifyV2避坑指南与调试心得

本文详细解析了TikTok滑块验证码verifyV2的破解与优化策略,涵盖验证码触发机制、滑块图片获取、验证请求构造及高级调试技巧。通过实战案例和代码示例,帮助开发者有效应对复杂反爬机制,提升验证通过率。特别针对verifyV2系统的加密算法和轨迹模拟提供了专业解决方案。

weixin_30919429的博客 641

设计模式-桥接模式(Bridge)

桥接模式,也被称为柄体(Handle and Body)模式或接口(Interface)模式,是一种桥接模式,也被称为柄体(Handle and Body)模式或接口(Interface)模式,是一种结构型设计模式。它的主要目的是将抽象部分与它的实现部分分离,使它们都可以独立地变化。这种分离可以通过组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。举个例子,假设有一个几何形状Shape类,从它能扩展出两个子类:圆形Circle和方形Square。

miaoyl的博客 1万+

从光猫到机顶盒:H10G-13融合网关双系统网络配置全解析(含超密获取技巧)

本文详细解析了中国移动H10G-13融合网关的双系统网络配置,涵盖光猫路由与安卓电视盒子的协同工作。通过晶晨S905L3芯片的优化,设备实现高效网络接入与安卓9.0系统的定制化应用,特别适合酒店和民宿行业的智能化改造。文章还提供了超密获取技巧和线刷固件包的实用指南。

weixin_42525264的博客 945

设计模式【6】——桥接模式(Bridge 模式)

在开发过程中大家肯定都遇到过这样的问题:1)客户给了你一个需求,于是使用一个类来实现(A);2)客户需求变化,有两个算法实现功能,于是改变设计,我们通过一个抽象的基类,再定义两个具体类实现两个不同的算法(A1 和 A2);3)客户又告诉我们说对于不同的操作系统,于是再抽象一个层次,作为一个抽象基类A0,在分别为每个操作系统派生具体类(A00 和 A01,其中 A00 表示原来的类 A)实现不同操作系统上的客户需求,这样我们就有了一共 4 个类。

sinat_42974726的博客 1317

树莓派4B+红外传感器:手把手教你DIY一个能走黑线的智能小车(Python代码全解析)

本文详细介绍了如何使用树莓派4B和红外传感器DIY一个智能循迹小车,包括硬件配置、GPIO连接、Python代码实现及优化算法。通过动态PWM调速、状态机设计和赛道记忆算法,提升小车的循迹性能和稳定性。适合初学者和电子爱好者动手实践。

weixin_29188043的博客 268

医药行业SAP序列号管理深度解析:如何满足GMP合规要求

本文深度解析了医药行业如何利用SAP序列号管理构建满足GMP合规要求的数字化追溯体系。文章详细阐述了SAP序列号在实现从生产到患者全流程“一物一码”精准追溯中的核心作用,并探讨了其与质量管理、冷链监控等系统的关键集成配置,为医药企业应对严格监管、保障产品质量与安全提供了切实可行的技术方案。

backprop5master的博客 655

【设计模式】【结构型模式】桥接模式(Bridge)

桥接模式(Bridge Pattern)是一种结构型设计模式,核心思想是**将抽象与实现分离,让它们可以独立变化。**简单来说,它像一座“桥”连接了两个维度的变化,避免用继承导致代码臃肿。解耦抽象与实现抽象层和实现层可以独立变化,互不影响。例如,JDBC中Connection接口(抽象层)和Driver实现(实现层)可以分别扩展。提高扩展性新增抽象类或实现类时,无需修改现有代码,符合开闭原则。例如,新增数据库驱动只需实现Driver接口,无需修改JDBC核心代码。减少类爆炸。

flzjkl的博客 1124

UE5实现建筑剖切效果

UE5实现建筑剖切效果

专注GIS开发 8462

深入理解 Docker 网络:Bridge、Host、Overlay 与 None 模式详解

Bridge(桥接)网络是 Docker 默认的网络模式。每个容器通过一个虚拟网卡(veth pair)连接到 Docker 创建的网桥(通常是docker0容器间可通过容器名✅ 自定义 bridge 网络支持容器 DNS 自动解析,非常适合在单机环境中运行多个服务。Host 模式是让容器直接使用宿主机的网络栈,不再分配独立的网络命名空间。Overlay 网络是 Docker Swarm 或 Kubernetes 使用的核心机制。它基于 VXLAN 隧道实现跨主机容器互联。None 模式。

CharlesYuangc的博客 1268

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

再谈bridge模式理解小例子

今天回头看了原来写的bridge模式的文章,感觉还是太泛化、理论,不能够方便快速的理解,下面就借这《大话设计模式》中有关bridge模式的解释来谈谈实际实际应用解决的问题。 手机品牌和软件是两个概念,不同的软件可以在不同的手机上,不同的手机可以有相同的软件,两者都具有很大的变动

技术改变人生 3707

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

设计模式之 Bridge 通俗理解

1 Bridge 模式定义 将抽象和行为划分开来,各自独立,但能动态的结合。 任何事物对象都有抽象和行为之分,例如人,人是一种抽象,人分男人和女人等;人有行为,行为也有各种具体表现,所以,“人”与“人的行为”两个概念也反映了抽象和行为之分。 在面向对象设计的基本概念中,对象这个概念实际是由属性和行为两个部分组成的,属性我们可以认为是一种静止的,是一种抽象,一般情况下,行为是包含在一个对象中;但是,在有的情况下,我们需要将这些行为也进行归类,形成一个总的行为接口,这就是桥模式的用处。 2 为什么使用?

码灵的博客 718

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

桥接(Bridge)模式理解

 桥接(Bridge)模式理解        在四人团的著作《设计模式》中,对桥接模式的意图只用了一句话:将抽象部分与它的实现部分分离,使它们都可以独立的变化。        理解这句话需要细细分析一番:       主要是理解“抽象部分与它的实现部分分离”。怎么理解呢?这里可以提出至少这样三个问题:Ø         什么是抽象部分?Ø         什么是实现

Marlon的专栏 1940

NAT模式 桥接模式bridge原理理解

桥接场景 无论现在的各种容器,还是我们之前常用的虚拟机,为了与局域网内其他IP通讯通常需要用到『桥接』。 Bridge桥接方式 Bridge 将虚拟机桥接到host机器的网卡上,guest和host机器都通过bridge上网.对外不同的ip。 NAT 想要理解Bridge首先要理解NAT. NAT是虚拟机借助宿主机的IP上网的,外人看来并不知道虚拟机的存在。 家用路由器一般都是NAT模式。 用户通过路由器发送数据 内网用户默认都是192.168.1.x网段,而路由器充当默认网关的角色,所有内网发出的包都将

nb_zsy的博客 5853

设计模式的理解:桥模式 Bridge

当一种抽象有多种实现时通常用继承来实现他们。桥模式的设计和装饰模式 有一定的相似之处,都是通过组合来解决继承的滥用问题。现有一个需求,将一个项目分成普通用户版和VIP版,该项目要同时支持WINDOW10 和手机版本。VIP版在当登录,发送消息时会有铃声提示和播放些动画,而普通用户版没有。基础功能抽象类如下: class Base { public: virtual void login(string userid,string password)=0; virtual void...

superSmart_Dong的博客 336

Android中Adapter和Bridge模式理解和应用

一 Adapter模式 意图:   将一个类的接口转换成客户希望的另外一个接口。 Adapter模式使得原本由于接口不兼容而不能在一起工作的那些类可以在一起工作。 适用性: 使用一个已存在的类,而它的接口不符合你的要求; 你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配他们的接口,而是使用对象适配器适配他们的父类接口; 你想创建一个可以复用的类,该类可以...

diaoju3333的博客 169

Docker实验室:深入理解Bridge网络模式

在Docker网络体系中,Bridge网络是最基础也是最常用的网络模式之一。本文将带您深入探索Docker的Bridge网络工作原理,通过实践操作帮助您掌握Bridge网络的配置和使用方法。 ## 实验环境准备 **系统要求**: - 运行Docker 1.12或更高版本的Linux主机 - 推荐使用Ubuntu 16.04或更高版本 **工具准备**: - 安装bridge-utils工具

gitblog_00604的博客 465
上一篇: 扩展Struts
下一篇: vagrant box 相关调查记录
java_user
博客等级 码龄21年 11粉丝 40原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值