设计模式总结与对比:创建型、结构型、行为型模式的核心区别与适用场景

学完23种设计模式,很多人会问:到底怎么选? 说实话,我当年刚学完时也懵。每种模式都懂,但一遇到真实需求就犯选择困难症。后来带团队做重构,踩了不少坑,才慢慢摸清门道。

今天这篇总结,我不打算罗列概念。咱们换个角度——从“你到底想解决什么问题”出发,把三类模式掰开揉碎讲清楚。

一句话记住三类模式:

  • 创建型:帮你把“对象怎么造”这件事封装起来,调用方不用管 new 的细节。
  • 结构型:帮你把“类和对象怎么组合”设计好,让系统更灵活、更易扩展。
  • 行为型:帮你把“对象之间怎么协作、怎么分配职责”理清楚,降低耦合。

一、创建型模式:把“new”这件事管起来

创建型模式的核心就一个:封装对象的创建过程。说白了,就是不让调用方直接 new,而是通过某种机制去获取实例。

我个人习惯把创建型模式分成两类:

  • 单例、工厂方法、抽象工厂:关注“如何创建单个或一族对象”。
  • 建造者、原型:关注“如何创建复杂对象或克隆对象”。

举个例子。我在项目中遇到过这样一个场景:系统需要对接多个第三方支付渠道,每个渠道的初始化参数、签名算法都不一样。如果每次都在业务代码里 new,那一旦新增渠道,就得改一堆地方。后来我用抽象工厂模式,把每个渠道的支付对象创建逻辑封装到对应的工厂里。新增渠道时,只需要加一个工厂类,业务代码完全不用动。

避坑指南:我曾经在单例模式上栽过跟头——在分布式环境下用了简单的双重检查锁,结果因为指令重排序导致获取到未完全初始化的实例。后来加了 volatile 才解决。记住:单例在分布式场景下要考虑序列化、反射攻击和集群唯一性

模式 核心意图 典型场景
单例 确保一个类只有一个实例 配置管理器、连接池、日志记录器
工厂方法 定义一个创建对象的接口,让子类决定实例化哪个类 日志框架(不同日志级别创建不同处理器)
抽象工厂 创建一组相关或依赖的对象 跨平台UI组件(Windows按钮、Mac按钮)
建造者 分步骤构建复杂对象 SQL查询构建器、复杂订单对象
原型 通过克隆创建对象 对象拷贝、避免重复初始化开销

二、结构型模式:把类和对象“拼”出花来

结构型模式解决的是“如何组合已有的类或对象,让它们协同工作”。你想想看,很多时候我们不是缺功能,而是不知道怎么把现有的模块优雅地拼起来。

这里我重点说三个最常用的:

  • 适配器:让接口不兼容的类能一起工作。说白了就是“转接头”。
  • 装饰器:动态给对象添加新功能,比继承更灵活。
  • 代理:控制对对象的访问,可以做延迟加载、权限校验等。

我记得有一次做旧系统改造,老代码里有一个日志类,只支持写文件。新需求要求同时写数据库。如果直接改老代码,风险太大。我用了适配器模式,写了一个适配器,把老日志类的接口转成新接口,再注入到新系统里。整个过程没动一行老代码。

注意:装饰器模式容易和继承混淆。我的经验是:如果功能扩展点很多且可能组合,用装饰器;如果扩展点固定且层次清晰,用继承。我曾经在一个项目中用装饰器给订单对象加各种折扣计算,结果嵌套了五六层,调试时根本看不清调用链。后来改用策略模式+组合,反而更清晰。

模式 核心意图 典型场景
适配器 让接口不兼容的类协同工作 老系统接口适配、第三方SDK封装
桥接 将抽象部分与实现部分分离 跨平台图形绘制(形状+颜色)
组合 将对象组合成树形结构表示“部分-整体” 文件系统、组织架构树
装饰器 动态给对象添加职责 IO流(BufferedInputStream)、权限校验
外观 为子系统提供统一的高层接口 复杂库的简化入口(如SLF4J)
享元 共享细粒度对象,减少内存占用 字符串常量池、游戏中的子弹对象
代理 控制对对象的访问 延迟加载、远程代理、权限控制

三、行为型模式:把对象之间的“互动”管好

行为型模式是最难掌握的一类,因为它涉及对象之间的通信、职责分配和算法封装。说白了,就是解决“谁该做什么,怎么做,以及怎么通知别人”。

我个人觉得,行为型模式里最实用的是这三个:

  • 策略模式:把算法封装成独立的策略类,运行时可以互相替换。
  • 观察者模式:定义一对多依赖,一个对象状态变化,所有依赖者自动收到通知。
  • 模板方法模式:在父类定义算法骨架,子类实现具体步骤。

嗯,这里要特别说一下策略模式。我在做支付系统时,不同支付渠道的优惠计算逻辑完全不同。如果用 if-else,那代码会膨胀到没法维护。我用策略模式,把每种优惠算法封装成一个策略类,然后通过工厂获取。新增一种优惠时,只需要加一个策略类,再注册到工厂里就行。

一个容易混淆的点:策略模式和状态模式看起来很像,但意图不同。策略模式是“算法可替换”,客户端主动选择策略;状态模式是“状态驱动行为变化”,状态自动切换。我曾经在订单状态流转中用错了策略模式,结果状态切换逻辑散落在各个策略类里,后来改成状态模式才理顺。

模式 核心意图 典型场景
策略 定义一系列算法,封装起来,使它们可互换 支付优惠计算、排序算法选择
观察者 定义一对多依赖,状态变化时通知所有观察者 事件监听、消息推送、MVC架构
模板方法 在父类定义算法骨架,子类实现具体步骤 JdbcTemplate、工作流引擎
命令 将请求封装为对象,支持撤销、队列、日志 操作撤销、任务队列、宏录制
迭代器 提供一种顺序访问聚合对象元素的方法 集合遍历、数据库游标
中介者 用一个中介对象封装一组对象的交互 聊天室、GUI组件协调
备忘录 在不破坏封装的前提下,保存和恢复对象状态 游戏存档、编辑器撤销
解释器 为语言定义文法表示,并解释句子 正则表达式引擎、SQL解析
职责链 将请求沿着处理链传递,直到有对象处理它 审批流程、过滤器链
访问者 在不改变元素类的前提下,为元素增加新操作 编译器语法树遍历、报表生成

四、三类模式的核心区别:一张图看懂

说了这么多,咱们用一张图把三类模式的核心区别总结一下。这张图我画了很多遍,每次给团队培训都用它。

三类设计模式核心区别 创建型 关注:对象怎么造 • 封装 new 的细节 • 让调用方不依赖具体类 • 控制实例数量或创建过程 代表模式: 单例、工厂方法 抽象工厂、建造者 原型 适用:对象创建复杂 或需要控制实例数 结构型 关注:类和对象怎么组合 • 让类/接口协同工作 • 动态扩展对象功能 • 简化复杂子系统的访问 代表模式: 适配器、装饰器 代理、外观 桥接、组合、享元 适用:需要组合现有类 或统一访问接口 行为型 关注:对象怎么协作 • 封装算法和行为 • 对象间通信解耦 • 职责分配与流程控制 代表模式: 策略、观察者 模板方法、命令 职责链、中介者等 适用:算法多变 或对象间交互复杂 箭头表示关注点递进:从“怎么造”到“怎么组合”再到“怎么协作”

五、如何选择?我的实战经验

最后,分享几个我实际选型时的判断标准:

  1. 先看问题本质:是“创建对象麻烦”还是“组合对象麻烦”还是“对象协作麻烦”?对应到三类模式。
  2. 再看变化点:如果变化的是“算法”,用策略;如果变化的是“对象创建方式”,用工厂;如果变化的是“对象行为”,用状态或观察者。
  3. 不要为了用模式而用模式:我见过有人在一个只有三个类的项目里用了抽象工厂+单例+观察者,结果代码比不用模式还复杂。模式是工具,不是目的。
  4. 优先用组合,少用继承:结构型模式里,装饰器、适配器都是组合思想的体现。组合比继承更灵活,更容易应对变化。

一个小技巧:当你觉得代码里 if-else 太多,或者新增一个功能要改很多类时,大概率是设计模式没用好。这时候停下来,画一下类图,看看能不能用某个模式重构。我每次重构前都会问自己:“这个变化点,对应哪个模式的意图?”

好了,关于三类模式的总结就到这里。记住:模式是经验的结晶,不是教条。多在实践中体会,慢慢你就能形成自己的“模式直觉”。

公众号:蓝海资料掘金营,微信deep3321