Loading...

一篇文章带你回顾23种设计模式(附链接)

前言

从2024年1月写下第一篇《设计模式-单例模式》,到现在完成最后一篇《设计模式-访问者模式》,这个系列终于写完了。

整个系列按照23种设计模式的学习路线展开。因为简单工厂模式抽象工厂模式合并写在《设计模式-工厂模式》里,所以最后一共有22篇文章。每一篇都结合Java代码来讲,代码也来自我自己整理的design_patterns项目。

设计模式并不是必须照搬的代码模板,它更像是前人针对常见设计问题总结出来的一套解决思路。遇到相似问题时,我们可以借鉴这些思路,少走一些弯路。不过,模式也不是用得越多越好。能用简单代码解决的问题,就没有必要为了套模式而增加额外的类和接口。

这篇文章不再展开完整代码,而是按系列顺序回顾每一种设计模式解决什么问题,并附上原文链接,方便后面查阅。

一、创建型设计模式

创建型设计模式主要关心对象如何创建。它们把对象创建过程从业务代码中分离出来,让系统在创建复杂对象、切换实现或者管理实例时更灵活。

单例模式

单例模式保证一个类在程序中只有一个实例,并提供统一的访问入口。它适合管理需要全局共享的对象,例如配置管理器、日志记录器、线程池和数据库连接池

20260816160835571.webp

单例模式看起来简单,真正需要注意的是多线程环境下的安全问题,以及是否需要延迟加载。系列文章中分别介绍了懒汉式、饿汉式、静态内部类、双重校验锁和枚举几种实现方式。

目的地:设计模式-单例模式

工厂模式

工厂模式把对象创建集中到工厂中,客户端只提出需要什么,不直接依赖具体类的构造过程。以后增加或替换产品时,业务代码受到的影响会小很多。

20260816160858483.webp

本篇文章合并介绍了简单工厂模式和抽象工厂模式简单工厂根据参数创建不同产品,适合产品类型较少、变化不频繁的场景。抽象工厂负责创建一组相互关联的产品,适合同时切换整个产品族,例如一次性切换不同品牌或不同平台下的一套组件。

目的地:设计模式-工厂模式

建造者模式

建造者模式把复杂对象的构建过程拆成多个步骤,再由建造者按顺序完成组装。它把“怎么构建”和“最终得到什么对象”分开,同一套构建步骤也可以组合出不同结果。

20260816161100861.webp

当一个对象的构造参数很多,或者创建过程有明显先后顺序时,建造者模式会比堆叠构造方法更容易阅读。Java中的StringBuilder也是一个很熟悉的例子。

目的地:设计模式-建造者模式

原型模式

原型模式通过复制已有对象来创建新对象,不再从头执行完整的初始化流程。它适合对象创建成本较高,或者新对象与已有对象大部分内容相同的情况。

20260816161357377.webp

使用原型模式时,需要分清浅拷贝和深拷贝。对象中如果包含集合或其他可变引用,只复制最外层对象往往不够,否则多个对象可能仍然共享同一份内部数据。

目的地:设计模式-原型模式

二、结构型设计模式

结构型设计模式关心类和对象怎么组合。它们通常不会改变对象的核心职责,而是通过包装、转换、拆分或者共享,让已有代码更容易连接和扩展。

适配器模式

适配器模式用一个转换层连接两个接口不兼容的对象。客户端继续使用自己熟悉的接口,适配器负责把调用转换成旧接口或者第三方接口能够理解的形式。

20260816161417019.webp

它常见于旧系统改造、第三方SDK接入和不同数据格式之间的转换。适配器的作用不是重写原有功能,而是让原本无法直接配合的代码可以一起工作。

目的地:设计模式-适配器模式

桥接模式

桥接模式把一个类中两个可以独立变化的维度拆开,通过组合把它们连接起来。抽象部分负责对外能力,实现部分负责具体执行,两边都可以独立扩展。

20260816161427218.webp

例如图形和颜色、消息类型和发送渠道。如果全部依靠继承组合,每增加一种图形或渠道都可能产生很多新类。桥接模式可以避免这种类数量快速增长的问题。

目的地:设计模式-桥接模式

装饰器模式

装饰器模式在不修改原类的情况下,通过一层层包装给对象增加新功能。每个装饰器保持相同接口,因此可以自由组合,也可以在运行时决定添加哪些能力。

20260816161436965.webp

它适合日志、缓存、权限校验、数据压缩等可以叠加的功能。与大量子类相比,装饰器更灵活,不过包装层太多时,调用链也会变得难以排查。

目的地:设计模式-装饰器模式

外观模式

外观模式给复杂子系统提供一个统一、简单的入口。客户端不用了解内部每个模块的调用顺序,只需要调用外观类暴露的方法。

20260816161446097.webp

它很适合封装多步骤业务流程,也常用于给旧系统整理一个更清楚的对外接口。外观类负责降低使用成本,但不会限制客户端在需要时直接访问底层模块。

目的地:设计模式-外观模式

享元模式

享元模式把大量对象中重复且不变的数据提取出来共享,只为每个对象保留真正不同的部分。它的主要目的很直接,就是减少对象数量和内存占用。

20260816161502036.webp

文本编辑器中的字符样式、地图中的图标和游戏中的重复模型都可以使用这种思路。使用时需要分清内部状态与外部状态,共享对象本身最好保持不变。

目的地:设计模式-享元模式

代理模式

代理模式给真实对象提供一个替代访问入口。代理和真实对象实现相同接口,客户端看起来仍在调用原对象,实际调用会先经过代理处理。

20260816161546065.webp

代理可以完成权限校验、延迟加载、日志记录、远程调用和缓存等工作。Spring AOP中的很多能力也能看到代理模式的影子。

目的地:设计模式-代理模式

组合模式

组合模式把单个对象和对象集合组织成树形结构,并让客户端用相同方式处理叶子节点和组合节点。调用者不必反复判断当前拿到的是一个对象还是一组对象。

20260816161557111.webp

文件目录、组织架构、菜单和UI组件树都适合使用组合模式。它能让递归结构的处理代码更统一,但也要注意避免把不属于所有节点的操作硬塞进公共接口。

目的地:设计模式-组合模式

三、行为型设计模式

行为型设计模式主要处理对象之间如何协作,以及职责怎样分配。它们关注的不是对象长什么样,而是一次请求、一次状态变化或者一段流程应该由谁来完成。

责任链模式

责任链模式把多个处理者按顺序连接起来,请求沿着链条向后传递,直到某个处理者完成处理,或者整条链结束。

20260816161610326.webp

审批流程、过滤器、拦截器和异常处理都常用这种方式。发送请求的一方不用知道最终由谁处理,链上的节点也可以灵活调整。需要注意的是,一条过长的链会让请求过程不容易追踪。

目的地:设计模式-责任链模式

命令模式

命令模式把一次操作封装成独立对象,让发出命令的人和真正执行命令的人分开。命令对象可以保存参数,也可以被排队、记录、撤销或者重做。

20260816161702735.webp

菜单操作、任务队列、遥控器和事务记录都可以使用命令模式。它让调用过程更容易扩展,不过每一种操作通常都要增加一个命令类。

目的地:设计模式-命令模式

解释器模式

解释器模式为一套简单语法定义表达式对象,并通过这些对象解释输入内容。它适合规则较固定、语法规模较小的场景。

20260816161713819.webp

简单计算器、条件表达式和小型规则引擎可以使用这种模式。语法一旦变得复杂,类的数量和解析难度都会迅速增加,这时使用成熟的解析工具通常更合适。

目的地:设计模式-解释器模式

迭代器模式

迭代器模式提供一种顺序访问集合元素的方法,同时隐藏集合内部的存储结构。调用者只需要判断是否还有下一个元素,再读取当前元素。

20260816161723267.webp

这样一来,数组、链表或自定义集合都可以提供统一的遍历方式。Java集合框架中的Iterator就是迭代器模式最常见的实际应用。

目的地:设计模式-迭代器模式

中介者模式

中介者模式把多个对象之间复杂的相互调用集中到中介者中。各个对象不再直接了解所有协作者,只需要和中介者通信。

20260816161731562.webp

它适合聊天室、调度中心和界面组件联动等多对象协作场景。中介者可以降低对象之间的耦合,不过如果把所有规则都堆进去,中介者本身也容易变成一个过于庞大的类。

目的地:设计模式-中介者模式

备忘录模式

备忘录模式在不暴露对象内部实现的前提下,保存某个时刻的状态,并在需要时恢复。它相当于给对象创建一份状态快照。

20260816161739160.webp

编辑器撤销、游戏存档和表单草稿都适合使用这种方式。如果保存的状态很大或者快照过多,也要考虑内存占用和清理策略。

目的地:设计模式-备忘录模式

观察者模式

观察者模式建立一对多的订阅关系。被观察对象发生变化时,会主动通知所有已经注册的观察者,各个观察者再完成自己的处理逻辑。

20260816161749761.webp

它常用于事件通知、消息订阅和数据更新。观察者模式能减少发布者对具体接收者的依赖,但订阅关系较多时,需要处理重复通知、执行顺序和取消订阅等问题。

目的地:设计模式-观察者模式

状态模式

状态模式把对象在不同状态下的行为分别放进独立的状态类中。对象的状态发生变化后,同一个操作会执行不同逻辑,不需要在业务类里堆很多if和switch判断。

20260816161802341.webp

订单流转、工作流和设备状态控制都很适合使用状态模式。它把状态切换写得更清楚,不过状态数量较多时,也需要认真管理状态之间允许的转换关系。

目的地:设计模式-状态模式

策略模式

策略模式把可以互相替换的算法或处理方式封装成独立策略,再由上下文在运行时选择其中一种。调用方只关心要完成什么,不需要关心具体算法怎么实现。

20260816161813829.webp

支付方式、折扣计算、排序规则和路线选择都可以使用策略模式。它和状态模式在代码结构上很像,区别在于策略通常由外部主动选择,状态则更多是对象随着内部状态变化而切换行为。

目的地:设计模式-策略模式

模板方法模式

模板方法模式在父类中规定一套固定流程,把其中允许变化的步骤交给子类实现。这样既能保证整体执行顺序一致,又能给具体步骤留下扩展空间。

20260816161824614.webp

它适合流程稳定、部分细节不同的业务,例如数据导入、任务执行和游戏初始化。模板方法主要依靠继承,因此使用前也要确认这些实现之间确实存在稳定的共同流程。

目的地:设计模式-模板方法模式

访问者模式

访问者模式把作用于对象结构的操作放进访问者类中。对象元素只负责接收访问者,访问者负责针对不同元素执行不同逻辑。

20260816161833316.webp

它适合对象结构比较稳定,但后续经常增加新操作的场景,例如抽象语法树、报表节点和图形集合。新增访问者比较方便,新增元素类型则要修改所有访问者,这也是使用它之前需要重点考虑的地方。

目的地:设计模式-访问者模式

总结

回头看这22篇文章,可以把设计模式理解成三个方向:

  • 创建型模式解决对象怎么来的问题。单例控制实例数量,工厂隔离具体创建过程,建造者拆分复杂构建步骤,原型通过复制已有对象生成新对象。
  • 结构型模式解决对象怎么连接的问题。适配器负责接口转换,桥接拆开两个变化维度,装饰器给对象叠加功能,外观简化子系统入口,享元共享重复数据,代理控制对象访问,组合统一处理树形结构。
  • 行为型模式解决对象怎么协作的问题。责任链传递请求,命令封装操作,解释器处理简单语法,迭代器统一遍历,中介者管理多对象通信,备忘录保存状态,观察者发送变化通知,状态模式根据状态切换行为,策略模式替换算法,模板方法固定流程,访问者给稳定的对象结构增加新操作。

这些模式之间并没有高低之分,也不是每个项目都要全部用上。真正有用的是先看清问题,再选择合适的思路。代码中出现大量条件判断、对象创建散落各处、模块依赖越来越乱时,可以回头看看是否有某种模式正好对应这个问题。如果引入模式后,代码反而更难读,那就说明当前场景可能还不需要它。写完这个系列,我最大的感受是:

看懂定义只是第一步,自己动手实现一遍,再放进具体业务场景里比较,才能真正理解一种模式为什么存在。很多模式的类图看起来相似,但它们解决的问题并不一样。状态模式策略模式就是很典型的例子,代码结构接近,出发点却完全不同。

系列中的示例代码已经整理在Gitee:点击跳转

单例模式访问者模式,22篇文章到这里全部整理完毕。中间有过断更,也有过重新捡起来继续写的时候,好在最后还是给这个系列画上了句号。至此,设计模式系列文章完美结束。[胜利]

0

回到顶部