之前学到了设计模式中的创建型模式,这些模式主要是解决创建对象时的问题:
- 单例模式:创建全局唯一的对象
- 工厂模式:创建具有相同接口但是不同的对象
- 建造者模式:创建对参数有诸多要求的对象,并对参数进行检验
- 原型模式:如果对象的创建非常庞大,而不同对象之间的差异有限,可以使用 复制+更新 的方式完成创建
下面要说到的,是设计模式中的结构型模式,主要处理不同类和对象组合在一起的经典结构。
代理模式
- 代理模式的思想,是在不改变原始类的情况下,使用一个代理类为原始类提供一些额外功能的扩展
- 最简单的代理模式,是继承原始类,然后在每个需要扩展功能的地方添加扩展代码。但是这样做比较繁琐,因为我们需要对每个类都进行一次代理扩展
- 较好的解决办法是,利用语言的特性,对原始类进行动态代理
- 代理常用于开发非功能性需求,例如:监控、统计、鉴权、限流、缓存等
- 如果你熟悉python中的装饰器,你会发现它可以用于实现代理模式
桥接模式
- 定义:将抽象和实现结构,让他们可以独立变化
- 解释:如果一个类存在两个(或多个)独立变化的维度,我们通过组合的方式,让这两个(或多个)维度可以独立进行扩展。
- 举例:一个日志记录的类可以存在多种维度:记录那个功能的日志、将日志写入到哪里、日志写入的格式。
对这不同的三种维度,每种维度都可以有不同的实现:以写入格式这个维度来说,有很多种日志写入格式,你可以选择指定日志格式。
你会发现,日志记录有多个维度,每个维度又有不同的实现方式。如果你将这种实现关系全部固定,会有指数爆炸级别的实现方式。而如果你将这个实现拆分为两个级别:维度级别和如何实现这个维度,那你只需要使用两次组合就可以完成。 - 感悟:设计模式说到底就是解耦和扩展的艺术。而解耦的艺术在于抽象和组合。
装饰器模式
- 装饰器模式和代理模式的代码实现非常相似,都是对原始类提供一些额外的功能扩展。他们很像,以至于我第一次学习到代理模式的时候就觉得代理模式是python中装饰器的翻版。但是即使如此,他们之间还是存在一些细微的差别。
- 差别1:装饰器类是对原始类的功能的增强,这一点和代理模式是从定义上的不同
- 差别2:由于是对原始类的功能的增强,那你不能想当然地认为原始类只需要进行一次增强。那么对于不同功能增强,不同的装饰器要可以依次嵌套,这是在设计的时候需要注意的。
- 所以,你需要熟悉语言中对象调用方法的顺序,并且最好不要在装饰器模式中使用组合,而是使用多次继承的方式实现。
适配器模式
- 适配器模式主要对接口进行适配,让原本不能兼容的接口转换成可以兼容的接口,从而让原本无法一起工作的类可以一起工作
- 你可以将适配器当做一个转接头,它完成了不同接口之间的数据的转换。最贴切的一个例子就是USB接口,一个设备只要实现了USB的接口适配,就可以连接在各种有这个接口的机器上
- 当接口设计出现问题的时候,我们常常会考虑使用适配器,使用场景有:封装有缺陷的接口设计、统一多个类的接口、外部依赖转换、老接口的替代、适配不同数据的格式
门面模式
- 定义:门面模式为子系统提供一组统一的接口,定义一组高层的接口让子系统更易用。
- 例子:一个系统A提供a、b、c、d四个接口,系统B需要调用其中的a、b、c三个接口,门面模式会让A提供一个包含了a、b、c三个接口的接口x,给B使用
- 门面模式就像是迪米特法则的忠实践行者
- 门面模式有以下几点好处:提升易用性、减少调用(如果这个调用时rpc调用,可能会有不错的性能收益)、解决事务性问题等
组合模式
- 组合模式和之前提到的“利用组合而不是继承”中的组合是完全不同的概念
- 定义:将一组对象组织成树型结构,以表示“整体-部分”的形式,组合模式让客户端可以统一这种“单个-组合”的处理逻辑
- 说人话就是:如果一系列的对象可以组织成树这种数据结构,你可以统一单个对象(父节点)和组合对象(子节点)的处理逻辑,并通过递归调用简化实现
- 最典型的例子就是,查找一个文件夹下的文件数:文件系统是一个典型的树型结构,统计文件数可以递归统计子文件夹下的文件数完成
享元模式
- 当一个系统中存在大量重复的对象,且这种对象是不可变对象,那么可以在内存中只保存一份对象,其他所有的对象引用这个对象
- 例如,有一个百万人同事在线的象棋游戏平台,每局游戏都只会有最多32枚棋子。如果把一个棋子看做一个对象,这些重复的对象就完全可以设计成享元
- 你可以用对象工厂缓存复用的对象来实现享元
- 享元和单例、缓存池的概念有点像但是两者是不同的:享元是为共享而存在的,目的是节省内存。而单例和缓冲池的目的则不同。
- pyhton中的小范围的int类型就使用了享元模式,你可以自己试一下两个相同的字符串是不是享元?
网友评论