跳到主要内容

设计模式概述

🎯 为什么学习设计模式?编写高质量代码

学习设计模式的根本目的是编写高质量的代码。高质量的代码能够更好地应对需求变更、提高团队协作效率、降低维护成本。评判代码质量涉及多个维度,掌握设计模式有助于我们在这些维度上进行改进:

  • 可维护性 (Maintainability):代码易于修改和完善,修复 bug 或添加新功能时风险低、效率高。
  • 可读性 (Readability):代码清晰易懂,便于他人(或未来的自己)理解和协作。
  • 可扩展性 (Extensibility):当需求变化时,能够方便地通过扩展而非大量修改来增加新功能。
  • 灵活性 (Flexibility):代码能够适应不同的使用场景和需求。
  • 简洁性 (Simplicity):代码逻辑清晰、简单明了,避免不必要的复杂度 (KISS)。
  • 可复用性 (Reusability):减少重复代码,提高开发效率 (DRY)。
  • 可测试性 (Testability):代码易于编写单元测试,保障代码质量和方便重构。

设计模式是实现这些高质量目标的重要手段之一。

编程方法论

想要拥有编写高质量代码的能力,我们就要学习一些编程的方法论,其中就包括面向对象(我们可以把它看成是一种设计思想)、设计原则、设计模式、编程规范、重构技巧等。

  1. 面向对象的思想(基础):面向对象是一种编程思想,也是一种编程范式,现在比较流行的编程语言大部分都是面向对象编程语言,而大部分项目也都是基于面向对象编程风格进行开发的。
  2. 设计原则(指导方针):设计原则是指导我们代码设计的一些经验总结。在软件开发中,为了提高软件系统的可维护性和可复用性,增加软件的可扩展性和灵活性,程序员要尽量根据设计原则来开发程序,从而提高软件开发效率、节约软件开发成本和维护成本。
  3. 设计模式(是设计原则的具体实现):设计模式是针对软件开发中经常遇到的一些设计问题,总结出来的一套解决方案或这设计思路,大部分设计模式要解决的都是代码的可扩展性问题。
  4. 编程规范(提高代码的可读性):编程规范主要解决的是代码的可读性问题。编程规范相对于设计原则、设计模式,更加具体、更加偏重代码细节。
  5. 重构(面向对象的设计思想,设计原则、设计模式、编程规范):重构代码一词通常是指在不改变代码的外部行为情况下而修改源代码,有时非正式地称为“清理干净”。在极限编程或其它敏捷方法学中,重构常常是软件开发循环的一部分:开发者轮流增加新的测试和功能,并重构代码来增进内部的清晰性和一致性。

💡 什么是设计模式?

  1. 定义与起源:设计模式(Design Pattern)是软件开发人员在长期实践中,针对反复出现的特定设计问题所总结出的、经过验证的、可复用的解决方案或最佳实践。最著名的就是 GoF (Gang of Four) 在其经典著作《设计模式:可复用面向对象软件的基础》中提出的 23 种设计模式。

  2. 模式的本质:设计模式并非具体的代码或库,而是一种思考方式和设计思路。它描述了问题、解决方案、何时使用该方案以及其效果。

  3. 设计模式要素:通常一个完整的设计模式描述包含:

    • 模式名称 (Pattern Name):简洁地概括模式。
    • 问题描述 (Problem):该模式旨在解决的设计问题或场景。
    • 解决方案 (Solution):描述模式的组成部分、它们的关系以及职责。
    • 效果分析 (Consequences):模式的优缺点以及使用时需要权衡的因素。

🧩 设计模式与其他核心概念的关系

设计模式并非孤立存在,它与面向对象、设计原则、编程规范、代码重构等概念紧密相关,共同构成了提升软件设计能力的知识体系:

  1. 面向对象 (OO):是设计模式实现的基础。封装、抽象、继承、多态等特性为各种设计模式提供了构建块和实现机制。
  2. 设计原则:是更高层次的指导思想(如 SOLID)。它们指导设计模式的选择和应用,例如,开闭原则是策略模式、模板方法模式等模式的重要指导思想。遵循设计原则有助于判断何时以及如何恰当地应用设计模式。
  3. 设计模式:是针对特定问题的、具体的、可复用的解决方案。它通常体现了某些设计原则,并利用面向对象的特性来实现其目标,主要目的是提升代码的可扩展性、灵活性和可维护性。
  4. 编程规范:关注代码细节和可读性。无论是否使用设计模式,代码都应遵循良好的编程规范。设计模式的实现代码同样需要符合规范,以保证最终代码的质量。
  5. 代码重构:是将设计原则和设计模式应用到现有代码中以改进其结构和质量的主要实践活动。重构常常是为了引入某个设计模式,或者使代码更好地遵循某个设计原则。

理解这些概念的关系有助于更深刻地理解设计模式的价值和应用场景。

📚 设计模式分类

经典的 GoF 设计模式通常分为三类:

创建型模式

创建型模式包括:单例模式、工厂模式、建造者模式、原型模式。它主要解决对象的创建问题,封装复杂的创建过程,解耦对象的创建代码和使用代码。

单例模式

单例模式用来创建全局唯一的对象。一个类只允许创建一个对象(或者叫实例),那这个类就是一个单例类,这种设计模式就叫做单例模式。单例有几种经典的实现方式,分别是: 饿汉式、懒汉式、双重检测、静态内部类、枚举。

尽管单例是一个很常用的设计模式,在实际的开发中,也经常用到,但有些人认为单例是一种反模式,并不推荐使用,主要原因有以下几点:

  • 单例对 OOP 特性的支持不友好
  • 单例会隐藏类之间的依赖关系
  • 单例对代码的扩展性不太好
  • 单例对代码的可测试性不友好
  • 单例不支持有参数的构造函数

工厂模式、IOC 容器也可以保证全局唯一性,替代单例。

工厂模式

工厂模式包括简单工厂、工厂方法、抽象工厂这 3 种细分模式。简单工厂和工厂方法比较常用,抽象工厂的应用场景比较特殊,较少用到。

工厂模式用来创建不同但是相关类型的对象(继承同一父类或者接口的一组子类),由给定的参数决定创建那种类型的对象。实际上,如果创建对象的逻辑并不复杂,直接通过 new 来创建对象就可以了,不需要使用工厂模式。 当创建逻辑比较复杂,是一个 “大工程” 的时候,就考虑使用工厂模式,封装对象的创建过程,将对象的创建和使用相分离。

当每个对象的创建逻辑都比较简单的时候,推荐使用简单工厂模式,将多个对象的创建逻辑放到一个工厂类中。当每个对象的创建逻辑都比较复杂的时候,为例避免设计一个过于庞大的工厂类,推荐使用工厂方法模式,将创建逻辑拆分的更细,每个对象的创建逻辑独立到各自的工厂类中。

  • 封装变化:创建逻辑有可能变化,封装成工厂类之后,创建逻辑的变更对调用者透明。
  • 代码复用: 创建代码抽离到独立的工厂类之后可以复用。
  • 隔离复杂性: 封装复杂的创建逻辑,调用者无需了解如何创建对象。
  • 控制复杂度: 将创建代码抽离出来,让原本的函数或类职责更加单一,代码更加简洁。

依赖注入框架中,比如 Spring IOC 、Google Guice , 它用来集中创建、组装、管理对象,跟具体业务代码解耦,让程序员聚焦在业务代码的开发上,DI 框架已经成为了开发的必备框架。

建造者模式

建造者模式用来创建复杂对象,可以通过设置不同的可选参数,定制化地创建不同对象。

如果一个类中有很多属性,为了避免构造函数的参数列表过长,影响代码的可读性和易用性,可以通过构造函数配合 set() 方法来解决。但是,如果存在下面情况中的任意一种,就要考虑使用建造者模式了。

  • 存在必填属性
  • 属性之间有一定的依赖关系或者约束关系
  • 希望创建不可变对象,不能暴露 set() 方法

原型模式

如果对象的创建成本比较大,而同一个类的不同对象之间差别不大(大部分字段都相同),在这种情况下,可以利用对已有对象(原型)进行复制(拷贝)的方式,来创建新对象,以达到就节省创建时间的目的。这种基于原型来创建对象的方式就叫做原型模式。

原型模式有两种实现方式,深拷贝和浅拷贝。浅拷贝只会复制对象中的基本数据类型和引用对象的内存地址,不会递归地复制引用对象,以及引用对象的引用对象... 而深拷贝得到的是一份玩玩全全独立的对象。

所以,深拷贝比浅拷贝更加耗时,更加耗内存空间。

如果要拷贝的是不可变对象,浅拷贝共享不可变对象是没问题的,但对于可变对象来说,浅拷贝的到的对象和原始对象会共享部分数据,就有可能出现数据被修改的风险,也就变得复杂多了。

操作非常耗时的情况下,推荐使用浅拷贝,否则,没有充分的理由,不要为了一点点性能提升而使用浅拷贝。

结构型设计模式

结构型模式主要总结了一些类或对象组合在一起的经典结构,这些经典结构可以解决特定应用场景的问题。结构型设计模式包括:代理模式、桥接模式、装饰器模式、适配器模式、外观模式、组合模式、享元模式。

代理模式

代理模式在不改变原始类接口的条件下,为原始类定义一个代理类,主要目的是控制访问,而非加强功能,这是它跟装饰器模式最大的不同。一般情况下,代理类和原始类实现同样的接口。但是,如果原始类并没有定义接口,并且原始类代码并不是我们自己开发维护的。这种情况下,可以通过让代理类继承原始类的方式来实现代理模式。

静态代理需要针对每一个类都创建一个代理类,并且每个代理类中的代码都有点像模板式的 “重复”代码,增加了维护成本和开发成本。对于静态代理存在的问题,我们可以通过动态代理来解决。我们不事先为每个原始类编写代理类,而是在运行时动态地创建原始类对应的代理类,然后在系统中用代理类替换掉原始类。

代理模式常用在业务系统中 开发一些非功能性需求,比如:监控、统计、鉴权、限流、事务、幂等、日志。我们将这些附加功能与业务功能解耦,放到代理类统一处理,让程序员只需要关注业务方面的开发。

除此之外,代理模式还可以应用在 RPC 、 缓存等应用场景中。

桥接模式

桥接模式的代码非常简单,但是理解比较有难度,并且应用场景也比较有限,在实际的项目中并没有那么常用。

桥接模式有两种理解方式。第一种是“将抽象和实现解耦',让它们能独立开发。这种理解方式比较特别,应用场景也不多。另一种理解方式更加简单,等同于”组合优于继承“设计原则,这种理解方式更加通用,应用场景比较多,但是不管是那种理解方式,代码结构都是相同的,都是一种类之间的组合关系。

对于第一种理解方式,定义中的抽象指的并非是 抽象类 或 接口,而是被抽象出来的一套类库,它只包含骨架代码,真正的业务逻辑需要委派给定义中的 ”实现“ 来完成。而定义中的实现 也北非 ”接口实现类“,而是一套独立的 类库。 ”抽象” 和 “实现“ 独立开发,通过对象之间的组合关系组装在一起。

装饰器模式

装饰器模式主要解决继承关系过于复杂的问题,通过组合来替代继承,给原始类添加增强功能。这也是判断是否该用装饰器模式的一个重要的依据。除此之外,装饰器模式还有一个特点,那就是可以对原始类嵌套使用多个装饰器。为了满足这样的需求,在设计的时候,装饰器类需要跟原始类继承相同的抽象类或者接口。

适配器模式

代理模式、装饰器模式提高的都是跟原始类相同的接口,而适配器提供的跟原始类不同的接口。适配器模式是用来做适配的,它将不兼容的接口转换为可兼容的接口,让原本由于接口不兼容而不能一起工作的类可以一起工作。

适配器模式有两种实现方式: 类适配器和对象适配器。其中,类适配器使用继承关系来实现,对象适配器使用组合关系来实现。

适配器模式是一种事后的补救策略,用来补救设计上的缺陷。应用这种设计模式算是 无奈之举。如果在设计初期,就能规避接口不兼容的问题,这种模式就无用武之地了。

在实际的开发中,接口不兼容会出现下面 5 种场景:

  • 封装有缺陷的接口设计
  • 统一多个类的接口设计
  • 替换依赖的外部接口
  • 兼容老版本接口
  • 适配不同格式的数据

门面模式

门面模式原理、实现都非常简单,应用场景比较明确。它通过封装细粒度的接口,提供组合各个细粒度接口的高层次接口,来提高接口的易用性,或者解决性能、分布式事务等问题。

组合模式

组合模式和面向对象设计种的 ”组合关系(通过组合来组装两个类)“,完全是两码事。这里讲的 ”组合模式“,主要是用来处理树形结构数据。正因为其应用场景的特殊性,数据必须能表示成树形结构,这也导致了这种模式在实际的项目开发中并不那么常用。 但是,一旦数据满足树形结构,应用这种模式就能发挥很大的作用,能让代码变得非常简洁。

组合模式的设计思路,与其说是一种设计模式,倒不如说是对业务场景的一种数据结构和算法的抽象。其中,数据可以表示成树这种数据结构,业务需求可以通过在树上的递归遍历算法来实现。组合模式,将一组对象组织成树形结构,将单个对象和组合对象都看作树中的节点,以统一处理逻辑,并且它利用树形结构的特点,递归地处理每个子树,依次简化代码实现。

享元模式

所谓 ”享元“,顾名思义就是被共享的单元。享元模式的意图是复用对象,节省内存,前提是享元对象是不可变对象。

具体来讲,当一个系统中存在大量重复对象的时候,就可以利用享元模式,将对象设计成享元,在内存中只保留一份实例,供多处代码引用,这样可以减少内存中对象的数量,以起到节省内存的目的。

实际上,不仅仅相同对象可以设计成享元,对于相似对象,也可以将这些对象中的相同的部分(字段),提取出来设计成享元,让这些大量相似对象引用这些享元。

行为型设计模式

创建型设计模式主要解决 ”对象的创建“ 问题,结构型设计模式主要解决 "类或对象的组合" 问题,那行为型设计模式主要解决的就是 ”类和对象之间的交互“ 问题,行为型设计模式有 11 种,分别是: 观察者模式、模板模式、策略模式、责任链模式、状态模式、迭代器模式、访问者模式、命令模式、解释器模式、备忘录模式、中介者模式

观察者模式

观察者模式将观察者和被观察者代码解耦。观察者模式的应用场景非常广泛,小到代码层的解耦,大到架构层面的系统解耦,再或者一些产品的设计思路,都有这种模式的影子,比如,邮件订阅、RSS Eeeds、本质上都是观察者模式。

不同的应用场景需求下,这个模式也有截然不同的实现方式,有同步阻塞的实现方式,也有异步非阻塞的实现方式;有进程内的实现方式;也有跨进程的实现方式。同步阻塞是最经典的实现方式,主要是为了代码解耦;异步非阻塞除了能实现代码解耦之外,还能提高代码的执行效率; 进程间的观察者模式解耦更加彻底,一般是基于消息队列来实现,用来实现不同进程间的被观察者和观察者之间的交互。

框架的作用有隐藏实现细节,降低开发难度,实现代码复用,解耦业务代码和非业务代码,让程序员聚焦于业务开发。针对异步非阻塞观察者模式,可以抽象成 EventBus 框架来达到这样的效果。

EventBus 翻译为事件总线,它提供了实现观察者模式的骨架代码。可以基于此框架非常容易地在自己的业务场景种实现观察者模式,不需要从零开始。

模板模式

模板方法模式在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。模板方法模式可以让子类在不改变算法整体结构的情况下,重新定义算法中的某些步骤。这里的算法,可以理解为广义上的 业务逻辑,并不特指数据结构和算法中的算法。 这里的算法骨架就是模板,包含算法骨架的方法就是模板方法,这也是模板方法模式名字的由来。

模板模式有两大作用:复用和扩展。其中的复用指的是,所有的子类可以复用父类中提供的模板方法的代码。扩展指的是,框架通过模板模式提供的功能扩展点,让框架用户可以在不修改框架源码的情况下,基于扩展点定制化框架的功能。

除此之外,回调跟模板模式具有相同的作用:代码复用和扩展。在一些框架、类库、组件等的设计中经常会用到,比如 JdbcTemplate 就是用来回调。

相对于普通的函数调用,回调是一种双向调用关系。A 类实现注册某个函数 F 到 B 类, A 类在调用 B 类的 P 函数的时候, B 类反过来调用 A 类注册给它的 F 函数。这里的 F 函数就是回调函数。A 调用 B ,B 反过来又调用 A ,这种调用机制就叫做回调。

回调可以分为同步回调和异步回调。从应用场景上来说哦,同步回调看起来更像是模板模式,异步回调看起来更像是观察者模式。回调跟模板模式的区别,更多的是在代码的实现上,而非应用场景上。回调基于组合关系来实现,模板模式基于继承关系来实现。回调比模板模式更加灵活。

策略模式

策略模式定义一组算法类,将每个算法分别封装起来,让它们可以互相替换。策略模式可以是算法的变化独立于使用它们的客户端(使用算法的代码)。策略模式用来解耦策略的定义、创建和使用。实际上,一个完整的策略模式就是由这三部分组成的。

策略类的定义比较简单,包含一个策略接口和一组实现这个接口的策略类,策略的创建由工厂类来完成,封装策略创建的细节。策略模式包含一组可选策略,客户端代码选择使用那个策略,有两证确定方法:编译时静态确定和运行时动态确定。其中,运行时动态确定才是策略模式真正的作用所在。

在实际的项目开发中,策略模式也比较常用。最常见的应用场景是,利用它来避免冗长的 if-else 或者 switch 分支判断。它也可以像模板模式那样,提供框架的扩展点等等。实际上,策略模式主要的作用还是解耦策略的定义、创建和使用,控制代码的复杂度,让每个部分都不至于过于复杂、代码量过多。除此之外,对于复杂代码来说,策略模式还能让其满足开闭原则,添加新策略的时候,最小化、集中代码改动,减少引入 bug 的风险。

指责链模式

在职责链模式中,多个处理器依次处理同一请求。一个请求先经过 A 处理器处理,然后再吧请求传递给 B 处理器,B 处理器处理完成后再传递给 C 处理器,以此类推,形成一个链条。链条上的每个处理器各自承担各自的处理职责,所以叫做职责链模式。

在 GoF 的定义中,一旦某个处理器能处理这个请求,将不会继续将请求传递给后续的处理器了,当然,在实际的开发中,也存在对这个模式的变体,那就是请求不会中途终止传递,而是会被所有的处理器都处理一遍。

职责链模式常用在框架开发中,用来实现过滤器、拦截器功能,让框架的使用者在不需要修改框架源码的情况下,添加新的过滤、拦截功能。这也体现了对扩展开放、对修改关闭的设计原则。

迭代器模式

迭代器模式也叫做游标模式,它用来遍历集合对象。这里说的集合对象,也可以叫做 容器、聚合对象,实际上就是包含一组对象的对象,比如,数组、链表、树、图、调表。迭代器模式主要作用是解耦容器代码和遍历代码。大部分编程语言都提供了线程的迭代器可以使用,不需要从零开发

遍历结合一般有三种方式,for 循环、foreach 循环、迭代器循环。后两种本质上属于一种,都可以看作是迭代器循环。相对于 for 循环遍历,利用迭代器来遍历有 3 个优势:

  • 迭代器模式封装集合内部的复杂数据结构,开发者不需要了解如何遍历,直接使用容器提供的迭代器即可;
  • 迭代器模式将集合对象的遍历操作从集合类中拆分出来,放到迭代器中,让二者的职责更加单一。
  • 迭代器模式让添加新的遍历算法更加容易,更符合开闭原则。除此之外,因为迭代器都实现自相同的接口,在开发中,基于接口而非实现编程,替换迭代器也变得更加容易。

在通过迭代器来遍历集合元素的同时,增加或者删除集合中的元素,可能会导致某个元素被重复遍历或遍历不到。

状态模式

状态模式一般用来实现状态机,而状态机常用在游戏、工作流引擎等系统开发中。状态机又叫做有限状态机,它由 3 个部分组成:状态、事件、动作,事件也称为转移条件。事件触发状态的转移及动作的执行。不过,动作不是必须的,也可能只转移状态,不执行任何动作。

针对状态机,有三种实现方式

第一种是分支逻辑法。利用 if-else 或者 switch-case 分支逻辑,参照状态转移图,将每一个状态转移原模原样地直译成代码。对于简单状态机来说,这种实现方式最简单、最直接、是首选。

第二种实现方式叫查表法。对于状态很多、状态转移比较复杂的状态及来说,查表法比较合适。通过二维数组来表示状态转移图,能极大地提高代码的可读性和可维护性。

第三种实现方式是利用状态模式。对于状态机来说、状态转移比较简单,但事件出发执行的动作包含的业务逻辑可能比较复杂的状态机来说,首选这种实现方式。

访问者模式

访问者模式允许一个或者多个操作应用到一组对象上,设计的意图是解耦操作和对象本身,保持类职责单一、满足开闭原则以及应对代码的复杂性。

对于访问者模式,难点在于代码实现,在项目中应用这种模式,会导致代码的可读性比较差。所以,除非不得已,不要使用这种模式。

备忘录模式

备忘录模式也叫做快照模式,具体来说,就是在不违背封装原则的情况下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便恢复对象为先前的状态。这个模式的定义表述了两部分内容: 一部分是,存储副本以便后期恢复;另一部分是,要在不违背封装原则的前提下,进行对象的备份和恢复。

备忘录模式的应用场景也比较明确和有限,主要用来放丢失、撤销、恢复等。它跟平时常说的备份很相似。两者的主要区别在于,备忘录模式更侧重于代码的设计和实现,备份更侧重架构设计或产品设计。

对于大对象的备份来说,备份占用的存储空间会比较大,备份和恢复的耗时会比较久。针对这个问题,不同的业务场景有不同的处理方式。比如,只备份必要的恢复信息,结合最新的数据来恢复;再比如,全量备份和增量备份相结合,低频全量备份,高频增量备份,两者结合来做恢复。

命令模式

命令模式不常用,了解即可。

落实到代码实现,命令模式中用到最核心的实现手段,就是将函数封装成对象。这样就可以实现把函数当做参数进行传递。

命令模式的主要作用和应用场景,是用来控制命令的执行,比如,异步、延迟、排队执行命令、撤消重做命令、存储命令、给命令记录日志等。

解释器模式

解释器模式为某个语言定义它的语法(或者叫文法)表示,并定义一个解释器用来处理这个语法。

要想了解语言要表达的信息,必须定义相应的语法规则。这样,书写者就可以根据语法规则来书写句子(专业说法是表达式)阅读者根据语法规则来阅读句子,这样才能做到信息的正确传递。

解释器模式就是用来实现根据语法规则来解读句子的解释器。

解释器模式的代码实现比较灵活,没有固定的模板,它的代码实现的核心思想,就是将语法解析的工作拆分到各个小类中,依次来避免大而全的解析类。

中介模式

中介模式的设计思想跟中间层很像,通过引入中介这个中间层,将一组对象之间的交互关系(或者说依赖关系)从多对多(网状关系)转换为一对多(星状关系)。原来一个对象要跟 n 个对象交互,现在只需要跟一个中介对象交互,从而最小化对象之间的交互 关系,降低了代码的复杂度,提高了代码的可读性和可维护性。

观察者模式和中介模式都是为了实现参与者之间的解耦,简化交互关系。两者的不同在于应用场景上。

在观察者模式的应用场景中,参与者之间的交互比较有条理,一般是单向的,一个参与者只有一个身份,要么是观察者,要么是被观察者。而在中介模式的应用场景中,参与者之间的交互关系错综复杂,既可以是消息的发送者,也可以同时是消息的接收者。

🚀 如何学习与实践设计模式?

设计原则和思想是心法,设计模式是招式。掌握心法,以不变应万变,无招胜有招 。

设计的过程是现有问题后有方案,我们要先去分析代码存在痛点,比如可读性不好、可扩展性不好等等,然后再针对性地利用设计模式去改善。

设计模式要干的事情就是解耦,也就是利用更好的代结构将一大坨代码拆分成职责更单一的小类,让其满足高内聚低耦合等特性。创建型模式是将创建和使用代码解耦,结构型模式是将不同的功能代码解耦,行为模式是将不同行为代码解耦。而解耦的主要目的是应对代码的复杂性。设计模式就是为了解决复杂代码问题而产生的。

因此,对于复杂代码,比如项目代码量多、开发周期长、参与开发的人员多,前期要多花点事件在设计上。

而为了避免过度设计,在真正有痛点的时候,再去考虑用设计模式来解决,而不是一开始就为不一定实现的未来需要而应用设计模式。

  1. 学习路线建议

    • 理解原则:先掌握 SOLID 等核心设计原则,它们是理解模式背后思想的关键。
    • 从问题出发:理解每个模式要解决的具体问题比死记硬背结构更重要。
    • 结合案例:通过实际的代码示例和开源项目中的应用来学习。
    • 循序渐进:从常用模式开始,逐步深入。
  2. 常见误区

    • 生搬硬套:不考虑具体场景,强行应用模式。
    • 过度设计:为了用模式而用模式,引入不必要的复杂性。
    • 模式滥用:将某个模式视为"银弹",用到所有地方。
  3. 实践要点

    • 理解场景:深入分析问题,判断是否真的需要应用设计模式。
    • 权衡利弊:了解模式带来的好处以及可能引入的复杂性。
    • 持续实践:在实际项目中尝试应用和重构,在实践中加深理解。
    • 保持简洁:优先选择更简单的方案,只有在必要时才引入模式。