浅谈Bridge模式的意图与心机~~

本文深入探讨了Bridge模式,解释了其将抽象与实现解耦的意图。通过举例说明,阐述了解耦和抽象的基本概念,并讨论了在设计模式中封装的重要性。文章指出,Bridge模式解决了抽象和实现之间高耦合度的问题,提供了一种更灵活的扩展性。最后,介绍了如何通过将实现封装在抽象类中来实现解耦,从而避免类数量的爆炸性增长。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

说实话,第一次听到这个官方定义我的内心是崩溃的,好不容易初探端详的面向对象的思想直接在这一句话中毁灭殆尽,好吧,我承认,我花了很久才吃透了这个模式

Bridge模式即大名鼎鼎的桥接模式,在宝典《Gof》中的意图是如下定义的:将抽象与实现解耦,使它们可以独立地变化~~

在具体地聊这个问题之前,我们再来聊2个基本的概念:

解耦:即让各种事物相互独立地行事,或者至少明确地声明之间的关系

抽象:是指不同事物之间概念上的联系方式

对于抽象与实现,大多数人谈及此问题时之所以会迷糊是因为他们认为实现即抽象的构建,因此实现与抽象来说应当是相辅相成的关系,及抽象是实现的占位符,
何谈耦合与解耦~~~

实际则是我想错了,“这里”的实现所指即抽象类及其派生类用来实现自己所用的对象~~,(在没有理解Bridge之前即使这句话你绝壁还是蒙逼的)

不过如果你已经意识到了上面这句话与之前那句话之间的区别,别担心,其实你已经较之前跨出了历史性的一步。

桥接模式在《设计模式》解析中给出了极高的评价,被称作是最难理解的模式,但却也是一个极其符合设计模式2大原则的例子:“找出变化并封装之”和“优先使用对象聚集而不是类继承”。

应用设计模式并不是直接拿模板去拼装代码,最重要的是理解模式的意图与目的,知道模式所欲解决的问题本身是什么?这才是学习解决问题方法的正确姿势~~~

对于在Bridge模式所遇到的问题主要有如下2点:概念的抽象有变化;这些概念的实现方式存在变化。

如果举出一个例子的话相信会好理解很多~~:现在有2个打印工具程序,同时现在有N种图形可能将要被打印~~,你需要编写一个程序去完成使用这2个程序之一去打印图形。对于打印某一个图形的时候选择哪一种方式去打印该图形则无需该程序考虑,调用者可以自己协调~~

以Cricle为例:我们的程序既然可以打印图形Cricle,那么这个程序必然需要设置变量接受所有有关该图形基础信息,将这些所有信息包裹在一个数据类型中,很多人喜欢称这种行为作封装,的确根据c++的定义的确是这样,但是目前这样做并没有体现出封装行为在设计模式中真正的深层含义,我暂且将其称作“包裹”,除了这些基础信息之外,这个程序还将具有打印该图形的功能,这个功能需要用到一些打印工具的部分功能组合协助完成。对于打印这个功能,我们习惯称作方法。

UML如下所示:


现在我们很明显有可能遇到以下2个问题,假设当前使用的是打印工具1,图形为Cricle,当绘制图形发生变化时我们呢该如何处理呢?

粘贴复制然后加上一个分支?这很明显是个错误的决定,这个方法直接违反了开放封闭原则,顺便说一句:很多bug都是源于修改,你经常很难把握修改一段逻辑对你的程序的整体的改变及影响,又有时这就是一个火药桶。

既然学习了面向对象的方法,我们呢自然要有一些面向对象的意识~,我们考虑一下如下这种设计;


我们可以设计这样一个抽象类Shape,他用来代表所有的图形,然后所有的具体的图形例如Cricle之流都作为Shape的子类继承,这样做的话其实就是相当于我们封装了图形的信息,我们可以通过Shape来引用到任意一个具体的图形,而程序本身则完全无需关注具体图形的形状和细节,只需要在特定的时期直接使用即可,这就体现了封装的价值:隐藏,c++中将这个行为称作对数据的隐藏,但是其实封装隐藏的不仅仅是数据,准确的说应当是隐藏一切,任何可以实际表达或衡量的事物其实都可以被隐藏~~,(举个栗子:方法的隐藏就是c++中虚函数的使用)


同样的我们也设置一个类似的抽象类Draw用来表示具体的打印的方法工具;然后将每一种具体的绘制工具类用这个类作为占位符代替,可能很多人会说到:这2套绘制工具是本就存在而非我们设计的,接口,功能的实现方法都不同,最重要的是他们极有可能并不是共生与一个父类,而是2个独立的类,对于这种认知上功能上价值上相同但是实际细节不同的2个类,我们可以采用适配器模式,为他们适配一套共有的接口,这在日常代码生产中是极为常见和有效的处理方式~~。

现在我们便得到了如下2个组合:


这个时候就很显然的出现了问题~,我们该如何去组合这2个内容呢?
很明显;我们的封装出来的打印方法是在这个程序中是不能独立存在的,我们的打印方法没有方法去主动获取图形的信息,同时在认知上来说一个绘打印工具也的确没有责任去获取图形的信息,他只是一个被调用的根据具体参数实时做出对应行为的工具,我们于是产生了这样一个结构,让我们的图形类继承自工具类,这样所有的图形就都具有了打印的功能,所有的图形都可以完成自己的打印,对于一个图形包含自身打印或其他这一类对自身操作的方法,从责任上来说也的确是合适的,每个对象都只负责与自己有关的thing。
实际的UML如下:

相信一些敏锐的人已经发现这在这其中所蕴含的一些重大危机:

1:具体图形类与打印方法类之间的耦合度过高(每一种能够提出的新的组合都需要创建一个新类,每一种基础图形或打印方法的添加都将带来大量的类爆炸)

2:这类结构设计有可能会存在过多的冗余代码(由于接口的统一,实现方式经常会出现相似或完全相同,这种设计下会出现大量相同代码)

很明显这不是一种很好的设计!


这个时候就让我们试一试另一种设计吧:


这样也算是解除了打印版本1与打印版本2之间的代码冗余,不过不得不说的类爆炸的问题和Cricle和Rentangle之间的耦合仍然在~~~,和原来相比半斤八两~

肯定是会有更好的设计~

这里不得不说到软件设计的又一大原则,尽量使用类聚集而不是类继承,类继承所带来的类与类之间的关系是一种绝对的强耦合。

在这里我们不由地想到打印工具和图形本身都是可抽象的事物,现在耦合度过高的问题在于他们都有可能同时发生变化,如果有办法可以分离这2种变化就完美了。

接下来就将向你们展示最终版本的结构设计

将打印工具作为图形类的一部分内嵌入图形类之中,在这种设计之下,图形的变化与打印方式的变化便被分离了开来,无论添加多少种不同的图形或者是打印方式,都不会产生多种实现组合的创建问题,只需要关注其变化的部分~~,有没有感觉瞬间问题都变得清爽了起来~~

此刻对于开始的那句抽象以及它所使用的对象的实现的耦合的分离是否有所理解,在这里就是对于打印方法的变化和图形本身变化的分离,使得适配器的可扩展性大大提高~~

下面是源于一段来自《设计模式解析》的一段分析:

Bridge模式:

意图:将一组实现与另一组使用它们的对象分离

问题:一个抽象类蕴含多个实现,但是不能出现类数量爆炸的增长

解决方案:为所有实现定义一个接口,供抽象类的所有派生类使用

效果:实现与使用实现的对象的解耦合,提供了可扩展性,客户对象无需操心实现问题。

实现:1:将实现封装在一个抽象类中

            2:在要实现的抽象的基类中包含一个实现的句柄。(接口)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值