
设计模式这个话题几乎每个做开发的都绕不开。我记得刚工作那会儿项目里一谈到代码结构老同事嘴里蹦出来的全是工厂单例观察者我当时只能点头装懂回头偷偷翻 GoF 那本《设计模式——可复用面向对象软件的基础》越看越糊涂——书上全是类和对象的关系图跟手里的业务代码完全对不上。直到后来自己带项目、做系统重构踩了无数坑才慢慢明白设计模式不是让你背的是拿来解决问题的。这篇是设计模式系列的第一篇我先把骨架打清楚重点解决认知层面的问题设计模式到底分几类创建型、结构型、行为型这三类分别解决什么问题它们之间怎么区分以及几个最容易混淆的高频模式——简单工厂、工厂方法、抽象工厂的关系观察者和发布订阅的微妙差别单例模式的几种写法之争。不管你用的是 Java、C 还是别的面向对象语言这套分类体系都是通用的。这篇文章适合两类人一是刚学完语法基础、准备提升代码设计能力的同学二是写了两三年业务代码但没系统梳理过设计模式的开发者。把分类搞清楚了后面深入每个模式的时候才不会迷路。1. 设计模式到底是什么——先弄清我们讨论的前提聊分类之前得先把设计模式这四个字的含义对齐。很多人以为它是一种具体的代码写作方式其实不是。设计模式是在特定上下文中针对反复出现的设计问题总结出来的一套可复用的解决方案模板。重点在模板两个字——它不是一段可以直接复制的代码而是一种思路、一种结构拿到你的业务场景里需要自己落地。1.1 设计模式的本质与起源设计模式这个概念最广为人知的出处是 1994 年出版的《设计模式可复用面向对象软件的基础》作者是 Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides 四个人江湖人称 GoFGang of Four四人组。他们把当时面向对象设计中反复出现的一些经典结构做了归纳总结最终整理出 23 个模式也就是我们现在常说的经典设计模式。但这 23 个模式不是他们凭空发明的而是从大量实际项目中提炼出来的最佳实践。打个比方你装修房子的时候发现所有的户型图几乎都有一个固定的动线设计——进门要有玄关、客厅和餐厅要连通、卧室要安静。这个动线设计就是模式它不规定你的沙发摆什么颜色、用实木地板还是瓷砖但它告诉你符合这套动线逻辑的装修住起来一定顺手。设计模式就是这个道理它不关心你的具体业务逻辑但它提供了一套经过验证的类与对象协作结构能让你的代码在扩展、维护、可读性上表现更稳。我第一次读 GoF 的时候最大的困惑是这 23 个模式全学完有什么意义实际上日常开发中你高频用到的可能就七八个其余更多是给你积累代码嗅觉。当你看到一个项目的类结构时你能识别出哦这里是策略模式这就已经值回票价了——因为你拥有了行业内共同的沟通词汇。1.2 设计模式要解决的三大痛点为什么需要设计模式因为面向对象程序写到一定规模会暴露三个典型的痛点而设计模式正是针对它们开出的药方。第一是可维护性。一个类堆了几千行改一个功能要牵动十几个地方任何人都害怕动这个代码。设计模式通过合理的职责划分和依赖关系管理让类的粒度更合理、模块边界更清晰。比如策略模式把不同的算法各自封装成独立的类你想加一种新策略只需要新增一个类注册进去不需要把原来的大 if-else 搬出来重写。第二是可扩展性。软件需求的变化是必然的一个好的设计应该在不修改已有代码的前提下支持新增功能这就是面向对象设计原则里的开闭原则。设计模式很多都是围绕开闭原则设计的。典型如工厂方法模式你要扩展一种新产品子类去重写工厂方法就够了调用方的代码完全不用动。第三是可沟通性。这一点容易被大家忽略。一个团队里当你说这里我用一个观察者模式其他成员立刻能理解你想表达的结构和意图不需要读全代码才能猜。设计模式提供了一套通用的架构词汇这是团队的隐性效率。不过这里我要泼一盆冷水设计模式本身只是工具不是目标。为了用模式而用模式强行套模板会写出一堆过度设计的代码——这个后面专门有一节细聊。现在先把底盘打好记住设计模式的本质是识别变化、封装变化。分类是什么分类就是看这个模式到底在封装哪一种变化。2. 三大分类全景创建型、结构型、行为型GoF 把 23 个模式分成三大类创建型、结构型、行为型。这个划分依据不是随意的而是对应了面向对象系统的三个核心环节对象怎么创建、对象怎么组合、对象怎么协作。理解了这条主线你就能很自然地记住哪些模式属于哪一类。2.1 创建型模式——对象怎么来的创建型模式解决的是如何创建对象的问题。听起来很简单new 一个不就完了但实际系统中对象的创建往往是复杂且会变化的有的对象要传一堆初始化参数有的对象全局只能有一个实例有的对象要按条件决定到底创建哪个子类。如果把这些逻辑全部散落在业务代码里一旦创建规则变了就要到处改。创建型模式的核心思路是把创建对象这件事从业务代码里剥离出来交给专门的机制去管理。经典的有 5 个模式工厂方法模式定义一个创建对象的接口让子类决定实例化哪个类。抽象工厂模式提供一个接口创建一组相关或相互依赖的对象而无需指定具体类。单例模式保证一个类只有一个实例并提供全局访问点。建造者模式将一个复杂对象的构建过程与它的表示分离同样的构建过程可以创建不同的表示。原型模式通过复制现有实例来创建新对象而不是通过 new。用生活场景类比你去餐厅吃饭不需要知道后厨是怎么洗菜、切菜、炒菜的你只需要告诉服务员要一份红烧肉菜就上来了。服务员和厨房就是一层创建隔离这叫简单工厂思想。如果你去的是连锁店每家分店对红烧肉的做法略有不同你只需要选择去哪家店具体哪家店怎么做你不用管这就是工厂方法。而今日套餐把汤、主菜、甜点打包一起上并且不同店打包的方案不一样这就是抽象工厂——它管理的是一族产品的创建。创建型模式是三大分类里最容易上手的一类尤其单例模式和工厂方法在业务系统里出镜率极高。后面的实战章节我会专门拆解它们。2.2 结构型模式——对象怎么合作结构型模式解决的是如何组织类和对象形成更大的结构问题。它们关注的是类与对象之间的组合关系怎么在满足低耦合的前提下让多个对象协同工作形成一个灵活的整体。经典的 7 个结构型模式适配器模式将一个类的接口转换成客户端期望的另一个接口让原本不兼容的类能一起工作。装饰器模式动态地给一个对象增加新的职责而不是通过继承去扩展。代理模式为另一个对象提供一个替身或占位符控制对这个对象的访问。外观模式为子系统的一组接口提供一个统一的门面接口简化使用。桥接模式将抽象部分与实现部分分离使它们都可以独立变化。组合模式将对象组合成树形结构以表示部分-整体的层次结构让客户端对单个对象和组合对象的使用保持一致性。享元模式通过共享技术有效支持大量细粒度对象的复用。结构型模式我经常用一个比喻乐高积木。积木块本身是独立的但你通过不同的拼插方式能搭出房子、汽车、飞船。适配器就像是积木里那种转换头能让两种不同规格的积木块接上装饰器就像你在积木房子外面裹了一圈灯光不用拆掉原来的结构就能增加装饰效果代理就像你请了一个管家别人要找房子主人先得经过管家这一关。结构型模式的核心价值在于在不改变对象内部实现的前提下改变对象的组织方式。这对系统的扩展性影响巨大。最典型的是适配器模式——你接手一个老系统里面有一个类的接口和新业务完全对不上又不可能去改老代码这时写一个适配器把老接口包装一下新老就能无缝对接改动最小、风险最低。2.3 行为型模式——对象怎么通信行为型模式解决的是对象之间如何分配职责、如何进行通信问题。到了这一层重点不再是对象怎么创建、怎么组合而是对象与对象之间的算法、流程、职责以及交互行为。经典的行为型模式有 11 个是三大类里数量最多的策略模式定义一系列算法把它们一个个封装起来并且使它们可以相互替换。模板方法模式定义一个操作中的算法骨架将一些步骤延迟到子类中实现。观察者模式定义对象间一对多的依赖关系当一个对象状态改变时所有依赖它的对象都得到通知并自动更新。迭代器模式提供一种顺序访问聚合对象元素的方法而不暴露其内部表示。责任链模式为了解除请求的发送者和接收者之间的耦合使多个对象都有机会处理请求并把这些对象连成一条链。命令模式将请求封装成对象以便使用不同的请求、队列或日志来参数化其他对象。备忘录模式在不破坏封装的前提下捕获并外部化一个对象的内部状态以便之后恢复。状态模式允许一个对象在其内部状态改变时改变它的行为看起来像是修改了它的类。访问者模式表示一个作用于某对象结构中的各元素的操作使你可以在不改变各元素类的前提下定义作用于这些元素的新操作。中介者模式用一个中介对象来封装一系列的对象交互使各对象不需要显式地相互引用。解释器模式给定一个语言定义它的文法的一种表示并定义一个解释器来解析该语言。行为型模式是三类里面最抽象、也最容易混淆的。比如状态模式和策略模式代码结构长得几乎一模一样但语义完全不同策略模式是让客户端自由切换算法状态模式是对象根据内部状态自动改变行为。再比如模板方法和策略模式都是一个骨架加可变实现差别在于模板方法是继承层次的复用策略模式是组合层次的替换。行为型模式解决的核心痛点就是职责分配和流程控制。举个大家写代码时都遇到过的例子一段代码里有大段的 if-else 判断每个分支执行不同算法而且分支还在不断增加。这时候用策略模式把这些分支算法拆成一个个策略类代码瞬间清晰。这就是行为型模式的威力——它把变化的行为和稳定的上下文拆开让行为可以独立演化。3. 分类之间的区别从三个维度看问题很多人学设计模式最大的障碍不是记不住单个模式而是分不清这个模式到底属于哪一类。其实三大分类的区别非常清晰你只要抓住三个维度关注点、变化点、抽象层次。3.1 关注点不同创建、组织、协作最直观的区分维度是关注点。拿一个完整的业务流程来看一个系统要运行必须先创建对象创建型然后把对象按照一定的结构组装起来结构型最后对象之间开始互相调用、传递消息、执行算法行为型。三个阶段分别对应三大分类。我用一张表把这层关系说清楚分类核心问题关注对象代表性模式创建型对象是怎么来的对象创建的时机、方式、过程工厂方法、单例、建造者结构型对象是怎么拼起来的类和对象的组织关系适配器、装饰器、代理行为型对象之间怎么协作职责分配、算法、通信策略、观察者、模板方法这个划分逻辑和面向对象系统的基本流程是一一对应的。你去看一个系统里哪些类负责创建其他对象它们多半和创建型模式有关哪些类负责组合接口、封装底层细节多半和结构型模式有关哪些类负责处理业务规则、算法流程多半和行为型模式有关。3.2 变化点不同封装什么类型的变化面向对象设计最高的心法是找到变化封装变化。三大分类的本质区别就是它们各自封装了不同类型的变化。创建型模式封装的是什么对象被创建的变化。你不希望调用方写死依赖某个具体类因为具体类一变调用方就要跟着变。于是你把创建哪个类这件事委托给工厂、建造者、原型去处理。调用方只依赖抽象接口具体创建什么由配置或者其他逻辑决定。结构型模式封装的是对象之间如何组合的变化。你不希望因为组合关系的变化就去修改对象本身的代码。适配器封装了接口不匹配的变化装饰器封装了职责扩展方式的变化代理封装了访问控制方式的变化。它们共同的特点是对象自己的核心逻辑不变变化的是它和外界的接线方式。行为型模式封装的是对象之间如何交互、算法如何执行的变化。策略模式把算法本身抽出来允许运行时替换观察者模式把通知机制抽出来让被观察者不需要知道具体有哪些观察者责任链模式把处理流程的组织方式抽出来允许你动态调整链条。它们封装的不是对象是谁也不是对象和谁有关而是对象做了什么、怎么做。这个维度特别适合用来做模式甄别。当你面对一个设计问题先问自己我要封装的变化是将来可能创建新的类是将来可能改变组合结构还是将来可能改变算法和行为逻辑对号入座归类就清楚了。3.3 模式之间的重叠与边界虽然分了三类但模式和模式之间确实存在边缘地带。经常有人问适配器和外观模式看起来差不多啊状态模式和策略模式的类图几乎一样有什么区别我专门整理几个高频混淆点。适配器和外观模式都涉及封装一个现存接口但动机完全不同。适配器是为了让原本不兼容的接口能对接核心目标是把别人的接口翻译成你需要的接口属于事后补救外观模式是为了简化一个复杂子系统的使用主动提供一个统一入口属于主动简化。适配器是客户不满意现成接口外观是客户根本不需要看到子系统的内部细节。状态模式和策略模式类图几乎一样差别在意图。策略模式的算法是被客户端主动选择的客户端知道自己在选策略状态模式是状态自动触发行为切换的客户端并不知道状态机的内部状态。打个比方策略模式是你去快餐店点餐你主动选汉堡还是炸鸡状态模式是你乘坐电梯电梯根据当前楼层和你按的按钮自动决定上行还是下行你不需要指挥它内部怎么运转。理解这些边界不是为了让你考试时做判断题而是为了避免开发时选错方案——选错了代码虽然能跑但扩展性差后面改起来想撞墙。4. 实战选型几个高频模式的对比与选择理论知识讲完了这一节横向对比几组最容易让人纠结的高频模式也是很多人在真实项目里反复踩坑的地方。我结合热词里提到的简单工厂模式Java 实现C 设计模式把几个高频场景掰开揉碎讲一遍。4.1 简单工厂 vs 工厂方法 vs 抽象工厂这三个模式放在一起几乎是所有设计模式教学里的第一道分水岭。很多人分不清它们根本原因在于它们长得太像都由一个工厂负责创建对象。但它们的抽象层级完全不一样。简单工厂不是 GoF 的 23 个经典模式之一但它是理解工厂系列的起点。简单工厂的做法是写一个工厂类里面有一个静态方法接收一个参数根据参数用 if-else 或 switch 返回不同的产品实例。public class SimpleFactory { public static Product createProduct(String type) { if (A.equals(type)) { return new ProductA(); } else if (B.equals(type)) { return new ProductB(); } throw new IllegalArgumentException(Unknown product type: type); } }优点是把创建逻辑集中到了一个地方缺点是每当新增产品类型都要改这个工厂方法违背了开闭原则。适用于产品类型比较少、扩展频率比较低的场景。工厂方法把创建对象这一步延迟到子类。父类定义一个创建产品的抽象方法子类各自实现自己负责的产品创建。调用方依赖的是父类接口具体实例化哪个类由子类决定。public abstract class Factory { public abstract Product createProduct(); } public class FactoryA extends Factory { Override public Product createProduct() { return new ProductA(); } } public class FactoryB extends Factory { Override public Product createProduct() { return new ProductB(); } }这么设计的好处是新增一个产品类型你只需要新增一个具体的工厂子类不用改动已有的工厂代码完全符合开闭原则。代价是类的数量直接翻倍一个产品对应一个工厂类。适用于产品族比较多的系统。抽象工厂则是升级版它管理的是一族相关对象的生产而不是单个对象。比如一个跨平台 UI 框架既要创建按钮又要创建文本框还要创建对话框。在 Windows 下的整套实现是一组类在 Mac 下的整套实现是另一组类。抽象工厂提供一个接口让实现方一次性提供一整套产品。public interface UIFactory { Button createButton(); TextBox createTextBox(); } public class WindowsUIFactory implements UIFactory { public Button createButton() { return new WindowsButton(); } public TextBox createTextBox() { return new WindowsTextBox(); } } public class MacUIFactory implements UIFactory { public Button createButton() { return new MacButton(); } public TextBox createTextBox() { return new MacTextBox(); } }三者的核心区别我总结成一张表对比维度简单工厂工厂方法抽象工厂是否属于 GoF 23否是是抽象层级单层一个方法一层延迟到子类两层一族产品创建对象类型一种产品的不同变体一种产品的不同变体一族相关联的产品扩展方式修改工厂方法新增子类新增工厂实现产品数量单个产品单个产品多个关联产品典型场景工具类、简单配置框架中需要扩展产品跨平台 UI、数据库访问选型建议如果你的需求很简单只有几个产品类型而且确定短期内不会增长直接用简单工厂就够了不要折腾。如果系统有明确的产品扩展规划那就用工厂方法牺牲一点类数量换取扩展性。如果你要创建的不是单个对象而是一整套配套产品比如你既要数据库连接又要数据库查询器还要数据库事务管理器那就该上抽象工厂。4.2 单例模式的几种写法之争单例模式是笔试题、面试题里的常客Java 实现也有很多流派饿汉式、懒汉式、双检锁、静态内部类、枚举。很多学习者死记硬背用枚举最好但不知道为什么这一节说点能落地的。饿汉式在类加载时就创建实例线程安全因为 JVM 类加载机制保证了线程安全缺点是不管用不用都会创建public class Singleton { private static final Singleton INSTANCE new Singleton(); private Singleton() {} public static Singleton getInstance() { return INSTANCE; } }懒汉式在第一次调用时才创建实例但需要考虑线程安全问题。直接给 getInstance 加 synchronized 简单粗暴但并发性能差。双检锁可以优化——先判断一次实例为 null 再进入同步块进入同步块后再次判空。要注意volatile关键字不能少否则指令重排可能导致拿到未完全初始化的实例public class Singleton { private static volatile Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance null) { synchronized (Singleton.class) { if (instance null) { instance new Singleton(); } } } return instance; } }静态内部类是利用 JVM 的类加载机制实现延迟加载不仅线程安全而且没有同步开销是我在 Java 项目里最推荐的写法public class Singleton { private Singleton() {} private static class Holder { private static final Singleton INSTANCE new Singleton(); } public static Singleton getInstance() { return Holder.INSTANCE; } }枚举写法则最简洁而且天然防反射攻击、防反序列化破坏public enum Singleton { INSTANCE; }这里分享一个实际经验单例模式容易踩的坑往往不在单例的实现方式而在单例的生命周期和序列化。比如把单例对象放到缓存里序列化反序列化回来可能得到一个新对象再比如反射调用私有构造方法也能创建第二个实例。如果你所在的框架比如 Spring本身就在管 Bean 的单例生命周期你就没必要再手写一个单例类直接用框架的 Scope 就完了。能不用单例就不用单例这是很多资深工程师的经验——它本质上是隐藏了全局状态会本末倒置地增加耦合度。4.3 观察者 vs 发布订阅观察者和发布订阅模式在概念上经常被人混为一谈。严格来说观察者模式是 GoF 模式发布订阅是观察者模式的一种变体或者说进阶形态。观察者模式中Subject被观察者直接持有 Observer 的引用列表状态变化时直接调用每个 Observer 的 update 方法。这意味着观察者和被观察者是认识彼此的耦合虽然比硬编码低但仍然存在import java.util.ArrayList; import java.util.List; public class Subject { private ListObserver observers new ArrayList(); private int state; public void attach(Observer observer) { observers.add(observer); } public void detach(Observer observer) { observers.remove(observer); } public void setState(int state) { this.state state; notifyAllObservers(); } private void notifyAllObservers() { for (Observer observer : observers) { observer.update(state); } } }发布订阅模式则引入了一个事件总线作为中间层发布者和订阅者互不认识都由总线来调度。消息发布者只负责发布事件订阅者只负责订阅自己感兴趣的事件。因为多了一个中间层解耦更彻底但也引入了消息通信的额外开销和消息丢失的可能。日常选择的经验是如果在同一个进程内、对象关系比较明确、需要直接回调结果观察者模式就够了代码简单直观。如果涉及跨模块、跨系统或者一方完全不想知道另一方是否存在那就走发布订阅借助消息队列或事件总线。别一上来就上重型消息中间件观察者模式在单体应用里经常是完全够用的。5. 常见误区和排查思路实录最后一个部分分享一些项目里真实遇到的问题。设计模式的坑往往不在模式本身而在使用者的选择和使用时机上。5.1 过度设计什么时候不该用设计模式最典型的错误是拿着锤子把一切当钉子。有人学完工厂模式连创建一个简单的 DTO 都要写个工厂学完策略模式连两个 if 分支都要拆策略接口。结果类数量爆炸代码结构复杂到没人敢改。什么时候不该用设计模式一个简单判断标准当你的代码在未来很长一段时间内不会被扩展和变化时。设计模式的核心价值在于应对变化如果你确定这段代码已经写死了、未来根本不会改那朴素写法反而是最优解——更加简洁清晰。可以反过来想想设计模式的成本。每个模式都引入了额外的抽象层和类结构这意味着更多代码、更多文件、更高的阅读门槛。团队里一个新同学看你写的代码如果一层套一层谁都得疯。所以一个务实的建议是从最朴素的写法开始等真正出现第一个变化的征兆时再在重构中引入模式。这比一上来就堆满模式要健康得多。写到这里我想起一个重构过好多次的老项目最早是一大坨 if-else 处理各种单据类型后来加类型加到十几种代码膨胀到维护不动了才抽出策略模式。如果我在写首个版本时就硬塞策略模式反而会让早期的结构变得臃肿。设计模式的正确路径往往不是提前设计而是随着变化而演进。5.2 相似模式辨识命名混乱怎么办模式一多辨识问题就来了。除了前面讲的适配器/外观、状态/策略还有几组也常常让人头疼我整理成一张速查表方便对照容易混淆的模式核心区别装饰器 vs 代理装饰器动态增加新职责代理控制访问方式代理 vs 适配器代理保持接口不变适配器改变接口模板方法 vs 策略模板方法用继承固定算法骨架策略用组合动态替换算法观察者 vs 中介者观察者解决一对多通知中介者解决多对多对象交互的网状依赖工厂方法 vs 建造者工厂方法一次性创建对象建造者分步骤构建复杂对象原型 vs 工厂原型通过复制已有对象创建新对象工厂通过实例化类创建新对象碰到相似模式我建议你直接问自己三个问题这个模式要封装的变化是什么客户端调用时它感知到的是接口还是行为类之间的关系是继承还是组合把这三个问题过一遍基本能定位到正确的模式。5.3 语言差异Java 和 C 实现设计模式的不同设计模式是语言无关的但落到 Java 和 C 不同的语言特性上实现方式有明显差异这些差异如果没搞清楚很容易把一种语言的习惯带到另一种语言里写出来的代码怎么看都别扭。Java 中的接口和抽象类是基本语法所以 GoF 里依赖抽象而非具体的思路落地得很自然。工厂方法、策略、观察者这些模式用 interface 一定义实现起来很顺手。而 C 没有内置 interface 关键字通常用纯虚类包含纯虚函数的类来模拟接口。C 的多重继承虽然强大但也带来更多的设计复杂度指南中一般更倾向于用组合而不是继承去实现结构型模式。再看内存管理。Java 有垃圾回收单例模式里你不用担心实例被提前析构C 中就要考虑单例对象的析构时机通常要配合局部静态对象和合理的销毁顺序否则程序退出时可能出现崩溃。而且 C 支持模板编译期多态很多模式可以用模板在编译期完成绑定运行期零开销而 Java 的泛型只是类型擦除本质还是运行期多态。举个例子策略模式在 C 里可以用函数指针、std::function或者模板参数来做实现方式比 Java 更灵活。语言特性决定了模式的形态但不改变模式的意图。还有一个常见问题是Java 里大家爱用的注解加反射来实现工厂注册C 里没有对应的内置机制往往用的是宏、静态注册表或者在 main 函数里手动注册。不能说哪种更优只能说你得先对语言特性心里有数再谈模式落地。5.4 设计模式学习的实操建议最后聊一下怎么学这是很多人的心结。设计模式不是看一遍就能掌握的关键在用。我的建议是分三步走。第一步建立骨架。把这 23 个模式的分类记清楚能说出每个模式对应哪一类、解决什么问题、核心类结构是什么。不需要背代码只需要建立索引。第二步在真实代码里识别。去找一些优秀的开源项目或者拿你自己项目的代码尝试标注这里用了装饰器这里用了责任链。这个阶段不要动手改代码先用眼睛识别训练模式嗅觉。识别得多了你才能真正理解抽象的结构。第三步在重构中应用。自己找一个被各种 if-else 和复杂继承折磨的模块尝试用模式去重构。注意一定要是重构不是为了模式而模式。重构之前先明确这段代码的问题是什么——是创建逻辑太散是职责太混乱还是算法难以替换定位好了再选择对应的模式。我用这个路径带过不少新人基本两三个月就能从听过模式的状态进到能够独立选型的状态。设计这行最大的门槛不是知识量而是在合适的场景做出合适的选择的判断力判断力只能靠实践养出来光看书是没用的。就我个人来说设计模式的学习是一条螺旋上升的路。第一遍看觉得都是废话什么工厂、单例不就是封装一下吗第二遍写业务代码开始觉得某些代码写着别扭回头找到模式的解法恍然大悟。第三遍带项目、评审别人的代码才能真正聊出为什么这么设计更好。这一篇先把分类和区别讲透了后面我会按分类逐个深入讲实现细节和实际场景里的取舍。如果这篇文章让你对创建型、结构型、行为型之间的关系有了清晰的坐标系那它的目的就达到了。