设计模式(四)结构型模式

前言

结构型设计模式,主要研究:

  • 主要有哪些场景使用结构型设计模式;
  • 每种场景应该使用何种设计模式;
  • 以程序中的功能为核心,研究程序功能的组织结构。所以这一章,我们要把“功能结构”作为研究的核心。

下面分别对几种结构型模式加以说明。

1. 适配器模式

示例:适配器模式

使用场景

想要在系统中添加某个功能,类似的功能在其他项目中已经实现,而且经过分析,旧的接口可以经过适当的封装,转换成新的适用于当前系统的接口。这样可以最大程度地复用已有的模块。

原理

通过适配器将已有功能的旧的接口,转换为新的接口,从而实现使已有的功能为新的系统服务的目的。

不适用的情况

以下情况下,不要使用适配器模式:

  • 旧的接口无法转换成新的接口;
  • 写适配器的工作量几乎可以重写所需的功能时。
使用须知

适配器模式其实是一种取巧的做法,在使用时需要慎重考虑。已有功能通过适配器集成到新的系统后,如果出现问题或者需要进行功能扩展,其维护成本是一个需要考虑的问题。例如,已有功能是否使用了过时的技术,或者资料不全,都可能成为项目的风险所在。

本质

适配器模式本质上是通过将一接口封装成另一种接口,实现了模块功能复用。

2. 桥接模式

示例:桥接模式

使用场景

桥接模式是少用继承,多用组合的第一例。
拿菜鸟教程中的例子来说,假如我们现在要实现圆类,包括红色圆和绿色圆两种圆。
首先容易想到的方法是,先写一个圆基类,并添加一个虚(virtual)的绘制函数。然后分别派生出红色圆类和绿色圆类,在子类中分别重新实现绘制函数,将圆绘制成指定的颜色。
在这种简单的例子中,使用继承是没什么问题的。但是假如圆基类还有一个虚函数,此虚函数用于对圆进行旋转。旋转分为两种,顺时针和逆时针旋转。我们想要四种类型的圆:

  • 顺时针旋转的红色圆
  • 顺时针旋转的绿色圆
  • 逆时针旋转的红色圆
  • 逆时针旋转的绿色圆

如何使用继承来实现呢?难道要派生出四个子类吗?如果还有其他功能组合呢?要多少个类呢?这样下去无疑会导致“类的数量爆炸”问题。

原理

结构型模式是研究程序功能组织方法的设计模式。当程序中需要对几种功能相互组合时,应该用组合,不要用继承。
桥接模式下,对于每个功能,应该提取出一个接口类,这个接口类可以有不同的实现。而代表不同的功能接口,可以作为主体类(此例中为圆类)的成员变量,放在一起。需要什么功能,就new哪种接口子类,保存在接口变量中,这样就可以把不同的功能组合起来。
这样一来,类的数量会保持在最低水平。

相对于继承,桥接模式相当于把主体类中的每个虚函数都单独提取出来,构成 一个接口类。这个接口类连接了具体实现和主体类,所以这个模式叫桥接模式。这个名字其实不是很能反映此模式的内涵,其实叫“功能组合模式”更为贴切。

可能有的小伙伴会说,上面的功能其实用C语言实现不是更简单吗?

  • 用一个结构体代表圆,有半径、边框宽度等属性;
  • 每种功能就是一个函数,参数为圆的结构体。

这不就完成了吗?是的!
这个场景下,确实使用C语言实现更简单,不需要使用C++面向对象的任何特性即可实现。可以看出,C++的面向对象的特性,并不是万能的,并不是在所有情况下都是最优的,甚至有时候不如C语言简单直接。从另一个角度说明,大家在面向对象编程的时候,不要把自己的思维局限于面向对象,面向对象思想在一些时候,是不如面向过程的,甚至会把功能结构复杂化,设计到最后导致代码难以维护都是有可能的。

使用须知

桥接模式会导致代码中存在设计模式的代码,会增加代码的理解难度。相对来说,积极作用还是远大于副作用的。

本质

桥接模式的本质是:

如果项目中出现了功能组合的场景,使用继承封装功能是错误做法,要把功能单独提取出来分别封装好以后,再进行组合。


3. 过滤器模式

示例:过滤器模式

使用场景

现有一组对象,我们想要根据不同的过滤条件,筛选出符合条件的一部分对象。

一般写法

最简单粗暴的方法是,在需要进行筛选过滤的地方,直接遍历对象数组,在循环体内进行条件判断。这种写法的优点是简单直接,缺点是如果有多个地方需要使用筛选过滤功能,则需要在多个地方编写重复的代码,这会降低代码的复用性和可维护性。

推荐写法

使用过滤器模式,把筛选功能封装起来使用即可。接口也很好定义,输入对象数组,输出符号条件的对象数组。

本质

过滤器模式的本质是:

当需要从一组对象中过滤出一部分符合条件的对象时,可以考虑将每种过滤功能都封装为一个类,提高代码复用性和可维护性。

4. 组合模式

示例:组合模式

组合模式是一种树形的对象组织结构,在建造者模式已有相关说明,这里不再赘述。

5. 装饰器模式

示例:装饰器模式

使用场景

装饰器模式的研究对象有两个:

  • 已有功能
  • 扩展功能

当我们需要扩展一个类的功能,但是又不想直接在此类上进行改动时,一般的做法是使用继承来实现。继承出来的子类具有父类的属性和方法,在父类基础上可以添加新的功能。
除了继承,还可以使用装饰器模式。装饰器模式是指,新建一个扩展类,将被扩展类的对象作为扩展类的成员变量保存,在扩展类中,操作被扩展类,实现新的方法和功能。
简单来说,装饰器模式就是新建一个类把已有类的功能包装起来,实现新的功能。

使用须知

装饰器模式和继承各有优缺点,使用时要加以权衡,选择最优的方案。

本质

从本质上说:

装饰器模式通过将已有功能作为成员变量整个封装(包装)起来,扩展的新功能,和已有功能耦合最小,互不影响。

6. 外观模式

示例:装饰器模式

本质

这个模式比较简单不再赘述。其本质是:

将已有功能封装起来,提供更易于使用的接口,屏蔽更多实现细节,等于是加了一个中间层。

7. 享元模式

示例:享元模式

按照我们的分类,此模式应该属于创建型模式。它是借助工厂模式来实现一个对象缓冲池,减少对象数量 ,加速创建速度。这里不再赘述。

8. 代理模式

示例:代理模式

应用场景

代理模式就是中介模式。例如我们想要租房,原本租房是发生在租客和房东之间的事务,但中介的出现,虽然会有一定的费用(损耗),但是整个过程的推进会更加顺利。代理模式,用来将两个比较难以直接沟通的功能主体关联起来,实现二者交互。

使用须知

和租房找中介一样,只有在必要的时候才找,否则就是浪费资源,只有在非用不可的时候才建议使用代理模式。

本质

代理模式的本质是:

代理模式封装了沟通所需的所有功能,将两个难以直接沟通的功能主体连接起来,起到了桥梁作用。


结语

结构型模式,就是以功能为核心,研究功能转换、功能调用、功能组合、功能封装等各种情形下,将哪些功能封装到哪些类中,更加高效地实现需求,提升代码的可维护性。

posted @ 2022-05-22 11:21  撬动未来的支点  阅读(13)  评论(0编辑  收藏  举报