设计模式复习指南:从七大原则到23种模式的选型与避坑

发布时间:2026/10/24 13:39:05
设计模式复习指南:从七大原则到23种模式的选型与避坑 简介这份《大话设计模式复习》文档面向具备一定面向对象编程基础、工作1至3年的研发人员系统梳理设计模式的核心知识体系。内容从设计模式的定义、分类、特点与应用场景切入详细讲解单一职责、开放-封闭、依赖倒转、里氏代换、迪米特法则、合成/聚合及接口隔离七大设计原则并深入剖析抽象工厂、策略、外观、适配器、组合、单例、中介者七种常用模式每种模式均配以定义、UML类图、基础代码与实例说明同时覆盖其余十五种模式最后对比模式差异、总结选择维度并附练习题与模拟试卷辅助巩固。资源包为1个docx文档大小约3.66MB目录结构清晰便于按章节检索学习。目前已有191人学习。读者可借此掌握模式适用场景与实现细节提升代码可维护性、可扩展性与复用性并在实际项目中更准确地选择合适的设计模式。1. 设计模式为什么背了 23 个模式代码里还是一个 if-else 走天下很多人学设计模式的方式是背定义单例是保证一个类仅有一个实例策略是定义一系列算法并使其可互换。背完之后打开项目发现业务代码里依然是一坨 if-else新增一个折扣类型就改一次主流程改完还要回归测试所有分支。问题不在于模式没背熟而在于没人讲清楚模式到底在解决什么结构性问题。设计模式的本质不是代码模板而是对变化点的封装约定。每当你发现某段逻辑会因为需求变更而反复修改就应该把它抽出来用一套固定的协作结构隔离它。这份复习资料的价值在于它把 23 种模式按创建型、结构型、行为型三类拆开每种模式都配了 UML 类图、基础代码和真实场景实例并且专门做了易混淆模式的对比表。它回答的不是这个模式是什么而是这个模式在什么条件下比另一个更合适。2. 七大设计原则模式背后的约束条件2.1 为什么先讲原则再讲模式设计模式是术设计原则是道。如果跳过原则直接看模式你会觉得每个模式都像是一种编码风格偏好学完不知道什么时候该用哪个。但把七大原则放在前面逻辑就通了每个模式都是为了满足一条或多条原则而存在的。单一职责原则SRP说的是一个类只负责一件事。这条原则直接解释了为什么策略模式要把每个算法拆成独立的类而不是写在一个方法里用 switch 分支。开放-封闭原则OCP说的是对扩展开放、对修改关闭。抽象工厂模式之所以要定义抽象工厂接口就是为了在新增产品族时只加新类、不改旧代码。依赖倒转原则DIP说的是高层模块和低层模块都依赖抽象。工厂方法模式里客户端只持有抽象产品接口具体产品类怎么换都不影响调用方。里氏代换原则LSP容易被忽略但很关键子类必须能替换父类而不破坏行为契约。这条原则在策略模式里体现得最明显——每个具体策略类都实现了同一个接口上下文类不关心用的是哪个策略因为它们的契约一致。迪米特法则LOD说的是不要和陌生人说话一个对象尽量少了解其他对象的内部结构。中介者模式就是这条原则的极端应用同事类之间完全不直接通信所有交互都通过中介者转发。合成/聚合原则CARP说的是优先用组合而不是继承。装饰模式是这条原则的典型你不需要为每种功能组合创建一个子类而是把装饰器一层层套上去。接口隔离原则ISP说的是接口要小而专不要搞一个大而全的接口让所有实现类都去实现一堆用不到的方法。2.2 原则之间的冲突怎么处理这七条原则不是孤立的它们之间经常打架。比如你为了满足 SRP 把类拆得很细结果类数量爆炸类之间的依赖关系变得复杂反而违反了迪米特法则。你为了满足 OCP 大量使用抽象和继承结果继承层次太深又违反了合成/聚合原则。常见的处理思路是优先保证 OCP 和 DIP因为这两条直接决定了系统的可扩展性SRP 和 ISP 在类设计层面指导拆分粒度LSP 是正确使用继承的前提如果做不到 LSP 就不要用继承LOD 和 CARP 在模块间交互层面起作用用来控制耦合度。实际项目中不需要每条都做到极致关键是知道自己在哪个维度上做了妥协以及为什么妥协。注意原则是判断依据不是评分标准。一段代码违反了某条原则不一定是错的但如果它同时违反了三条以上大概率会在下一次需求变更时出问题。3. 抽象工厂与策略模式两个最常被误用的模式3.1 抽象工厂到底在抽象什么抽象工厂模式的定义是提供一个创建一系列相关或相互依赖对象的接口而无需指定它们的具体类。这句话的关键词是一系列和相互依赖。它不是创建单个对象而是创建一组必须搭配使用的对象。资料里给的数据库访问实例很典型IFactory接口声明了CreateUser()和CreateDepartment()两个方法SqlServerFactory和AccessFactory分别实现它们各自返回对应数据库的具体 User 和 Department 对象。客户端代码只持有IFactory引用运行时决定用哪个工厂整个数据访问层的实现就切换了。// 抽象工厂接口声明创建产品族的方法 public interface IFactory { IUser CreateUser(); IDepartment CreateDepartment(); } // 具体工厂SqlServer 实现 public class SqlServerFactory implements IFactory { public IUser CreateUser() { return new SqlserverUser(); // 返回 SqlServer 专用的 User 实现 } public IDepartment CreateDepartment() { return new SqlserverDepartment(); // 返回 SqlServer 专用的 Department 实现 } } // 客户端只依赖抽象接口不依赖具体实现 IFactory factory new SqlServerFactory(); IUser iu factory.CreateUser(); iu.Insert(user); iu.GetUser(1);这段代码里IFactory是抽象工厂IUser和IDepartment是抽象产品SqlserverUser和SqlserverDepartment是具体产品。客户端从头到尾没有出现过new SqlserverUser()这样的代码所有对象的创建都委托给了工厂。这就是依赖倒转原则的落地客户端依赖IUser接口而不是SqlserverUser类。抽象工厂的代价也很明确如果要新增一个产品等级结构比如再加一个IScore接口就需要修改IFactory接口以及所有具体工厂类。这违反了开放-封闭原则。所以抽象工厂适合产品族稳定、但产品族内部实现经常切换的场景比如多数据库支持、多主题 UI 渲染。3.2 策略模式为什么不是简单的 if-else 替换策略模式的定义是定义一系列算法将每个算法封装到独立的类中使它们可以互换。很多人第一次看到策略模式会觉得这不就是把 if-else 拆成了类吗确实从行为上看策略模式和 if-else 分支做的是同一件事——根据条件选择不同的处理逻辑。但区别在于结构。if-else 的问题不在于它本身而在于它把选择逻辑和执行逻辑耦合在了同一个方法里。每次新增一个分支你都要修改这个方法重新测试所有分支。策略模式把每个算法的实现独立出去上下文类只负责持有策略引用和调用策略方法新增算法只需要加一个新类上下文类完全不动。// 策略接口所有算法的公共契约 interface Strategy { void doAlgorithm(); } // 具体策略 A class StrategyA implements Strategy { public void doAlgorithm() { /* 算法A的实现 */ } } // 上下文持有策略引用委托执行 class Context { private Strategy strategy; public void setStrategy(Strategy s) { strategy s; } public void executeStrategy() { strategy.doAlgorithm(); } }资料里的商场收银系统实例把这个结构落到了具体业务上正常收费、满减、打折三种计价方式各自是一个策略类收银台是上下文。新增一种促销方式时只需要实现一个新的策略类收银台的代码不需要任何改动。策略模式的适用条件是算法经常变化且不同算法之间可以互相替换。如果算法很少变化或者算法之间差异太大无法抽象出统一接口硬套策略模式反而会增加类的数量和维护成本。3.3 抽象工厂和工厂方法的关键区别资料里专门做了对比抽象工厂是为创建一组相关对象提供接口工厂方法是为创建一类对象提供接口。换句话说工厂方法只有一个产品等级结构抽象工厂有多个。如果你只需要创建一种类型的对象用工厂方法就够了如果你需要创建的对象必须搭配使用比如 SqlServer 的 User 必须配 SqlServer 的 Department那就需要抽象工厂。4. 结构型模式适配器、组合、外观的协作机制4.1 适配器模式解决的是什么问题适配器模式的核心场景是你有一个已有的类它的接口和当前系统需要的接口不匹配但你不想或者不能修改这个已有类的代码。适配器在中间做一层转换把已有类的接口转换成目标接口。资料里把适配器和装饰器、代理放在一起对比这三者结构相似但意图完全不同。适配器是改变接口装饰器是增强功能但不改变接口代理是控制访问但不改变接口。判断标准很简单调用方看到的接口变了吗变了就是适配器没变就是装饰器或代理。4.2 组合模式与装饰模式的边界组合模式把对象组织成树形结构让客户端对单个对象和组合对象的使用具有一致性。装饰模式通过组合的方式给对象动态添加职责。两者都用了组合关系但目的不同组合模式关注的是部分-整体的层次结构装饰模式关注的是功能叠加。资料里的对比表指出组合模式的典型场景是文件系统、UI 控件树这类树形结构装饰模式的典型场景是 I/O 流处理、中间件管道这类需要层层包装的场景。如果你发现自己在写一个递归遍历的树形结构考虑组合模式如果你发现自己在写一堆可选的增强逻辑考虑装饰模式。4.3 外观模式的定位外观模式为子系统提供统一的入口简化客户端调用。它不改变子系统的功能只是把多个子系统的调用编排成一个高层接口。资料里把外观模式归入七大重点模式之一原因是它在实际项目中出现频率极高——任何分层架构的 Service 层本质上都是外观模式的应用。外观模式和中介者模式容易混淆。外观模式是单向的客户端通过外观调用子系统子系统不知道外观的存在。中介者模式是双向的同事类之间通过中介者通信中介者知道所有同事类同事类也知道中介者。简单说外观是简化调用中介者是解耦交互。5. 避坑学设计模式时最容易翻车的几个地方5.1 把模式当银弹到处硬套现象学完策略模式后把项目里所有 if-else 都改成了策略类结果类文件从 20 个涨到 80 个改一个需求要跳转五六个文件才能看懂完整逻辑。原因策略模式适合算法经常变化且需要运行时切换的场景。如果分支逻辑很少变动或者分支之间共享大量上下文状态拆成独立类反而增加了理解成本。解决先用变化频率和变化独立性两个维度判断。变化频率低、变化之间有关联的逻辑保留在原方法里更合适。只有当某个分支确实会独立演化时才值得抽成策略类。5.2 抽象工厂新增产品等级时改到崩溃现象用抽象工厂做了多数据库支持后来要加一个 Redis 缓存层发现IFactory接口要加方法所有具体工厂类都要改改完还要逐个测试。原因抽象工厂的固有缺陷——新增产品等级结构需要修改抽象工厂接口违反开放-封闭原则。这不是用错了而是抽象工厂本身的取舍。解决如果产品等级结构可能频繁扩展考虑用工厂方法替代抽象工厂或者把工厂接口拆成多个小接口接口隔离原则。如果产品族稳定但产品等级会增长可以在抽象工厂之上再加一层简单工厂来管理工厂的创建。5.3 单例模式在多线程下创建了多个实例现象单例类的构造函数里加了日志发现启动时打印了两次创建实例。原因经典的懒汉式单例没有加同步控制两个线程同时判断instance null为真各自创建了一个实例。解决用双重检查锁定DCL加volatile关键字或者直接用静态内部类/枚举实现。资料里虽然把单例归入剩余模式分析但单例的线程安全问题在实际面试和代码审查中出现频率极高值得单独花时间搞透。5.4 混淆策略模式和状态模式现象用策略模式实现了一个订单状态流转逻辑结果发现状态之间的转换条件散落在各个策略类里维护起来比 if-else 还乱。原因策略模式的策略之间是平等的、可互换的客户端主动选择策略。状态模式的状态之间有转换关系状态自己决定下一个状态是什么。用策略模式实现状态机等于把状态转换逻辑从集中式变成了分布式。解决如果对象的行为取决于它的状态且状态之间有明确的转换规则用状态模式。如果只是运行时选择不同的算法算法之间没有转换关系用策略模式。5.5 观察者和中介者分不清现象实现了一个事件通知系统一开始用观察者模式后来发现观察者和被观察者之间的依赖关系太复杂改成了中介者模式结果中介者类膨胀成了一个上帝类。原因观察者模式是一对多的广播关系被观察者直接持有观察者列表。中介者模式是多对多的星型关系所有同事类只和中介者通信。观察者适合事件通知中介者适合复杂交互的解耦。解决如果对象之间的交互关系是简单的发布-订阅用观察者。如果对象之间的交互关系是多对多且经常变化用中介者但要注意控制中介者的职责范围避免它变成什么都管的上帝类。6. 模式选择的判断框架从场景反推模式6.1 用变化维度做第一层筛选选择模式的第一步不是看模式定义而是识别当前代码里的变化点在哪里。变化点决定了你需要哪种类型的模式。如果变化点在对象怎么创建考虑创建型模式。对象创建逻辑复杂、需要根据条件选择不同实现用工厂方法或抽象工厂对象创建成本高需要复用用原型模式对象构建步骤多且顺序固定用建造者模式全局只需要一个实例用单例模式。如果变化点在对象怎么组合考虑结构型模式。接口不兼容需要转换用适配器需要动态添加功能用装饰器需要简化子系统调用用外观需要树形结构统一处理用组合需要控制对象访问用代理需要共享细粒度对象用享元需要分离抽象和实现用桥接。如果变化点在对象怎么交互考虑行为型模式。算法需要互换用策略状态需要流转用状态一对多通知用观察者多对多解耦用中介者请求需要沿链传递用责任链操作需要撤销用命令加备忘录算法骨架固定但步骤可变用模板方法数据结构需要遍历用迭代器需要对对象结构做操作用访问者。6.2 用对比表做第二层确认资料里整理了多组易混淆模式的对比这些对比表是模式选择时最有价值的参考。比如策略模式 vs 状态模式策略模式的客户端知道所有策略并主动选择状态模式的状态转换对客户端透明。观察者模式 vs 中介者模式观察者是单向通知中介者是双向协调。适配器 vs 装饰器 vs 代理适配器改变接口装饰器增强功能代理控制访问。这些对比的本质是结构相似的模型意图不同适用场景就不同。判断时不要看代码结构要看谁在什么时候决定调用哪个类以及调用方对被调用方的了解程度。6.3 一个实际项目的模式组合案例资料里的可移植数据访问程序同时用到了抽象工厂、单例和外观。IFactory是抽象工厂负责创建 User 和 Department 对象族具体工厂类可以用单例模式保证全局只有一个实例数据访问层对外暴露的 Service 接口是外观模式隐藏了工厂创建和对象操作的细节。这个组合的逻辑是外观模式提供统一入口抽象工厂负责对象创建的解耦单例控制工厂实例的数量。三个模式各司其职没有重叠。如果你在项目中发现自己用了一个模式就能解决所有问题大概率是模式用错了如果用了一堆模式但说不清每个模式解决了什么变化点大概率是过度设计。6.4 验证模式是否用对的三个问题第一个问题如果新增一种同类需求我需要改几个文件用对了模式答案通常是只加一个新类不改旧代码。如果需要改接口或改调用方说明抽象层次没找对。第二个问题如果删掉这个模式代码会变成什么样如果删掉之后只是多了一个 if-else 分支说明这个模式带来的收益有限。如果删掉之后需要修改多个调用方、引入大量条件判断说明这个模式确实隔离了变化。第三个问题团队里其他人能不能在不看文档的情况下理解这个模式的使用意图如果类名和接口名足够清晰比如CashNormalStrategy、CashReturnStrategy说明模式用对了。如果类名是HandlerA、HandlerB说明抽象没做到位。我自己的习惯是每次引入一个模式之前先写一段最朴素的实现然后列出未来可能的变化点再判断这个模式能不能让变化点的影响范围最小化。如果答案是否定的就不引入。从那以后我每次做模式选型都强制走一遍变化点识别 → 模式匹配 → 对比确认 → 影响范围验证这个流程踩坑次数明显少了。希望帮到你。这份复习资料把 23 种模式的定义、UML 类图、基础代码、实例和对比表都整理在了一起适合在选型时快速翻阅对照。需要完整文档的话可以顺着这份资料的目录结构去获取。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询