前言
设计模式:从现象到本质的思维旅程
第一阶段:理解设计模式的本质与分类
- 设计模式到底是什么?为什么需要它们?
- 设计模式是解决常见问题的通用方案,帮助我们写出更可维护、可扩展、可复用的代码。
- 分类:创建型、结构型、行为型
- 面向对象设计原则与设计模式的关系
- SOLID 原则:单一职责、开闭原则、里氏替换、接口隔离、依赖倒置
- DRY(Don't Repeat Yourself)、KISS(Keep It Simple, Stupid)、YAGNI(You Aren't Gonna Need It)
- 设计模式的历史与发展
- GoF(Gang of Four)经典设计模式的起源与演变
- 现代 JavaScript 中的设计模式实践
第二阶段:创建型模式——如何优雅地创建对象?
单例模式(Singleton)
- 什么是单例模式?为什么要使用它?
- 确保一个类只有一个实例,并提供全局访问点。
- 场景:配置管理器、日志记录器、数据库连接池
- 实现方式与注意事项
- 使用闭包或 ES6 模块系统保证唯一性
- 多线程环境下的同步问题(JavaScript 不涉及)
工厂方法模式(Factory Method)
- 工厂方法模式的核心思想是什么?
- 定义一个用于创建对象的接口,让子类决定实例化哪一个类。
- 场景:UI 组件工厂、数据解析器
- 实现示例与优缺点分析
- 简单工厂 vs 工厂方法:灵活性 vs 实现复杂度
- 如何避免“上帝对象”?
抽象工厂模式(Abstract Factory)
- 抽象工厂模式与工厂方法的区别在哪里?
- 提供一系列相关或相互依赖对象的接口,而无需指定具体类。
- 场景:多平台 UI 开发、游戏引擎中的资源管理
- 实现与应用场景
- 创建多个相关对象的工厂家族
- 如何应对未来需求变化?
构建者模式(Builder)
- 构建者模式解决了什么问题?
- 将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。
- 场景:复杂的表单验证、SQL 查询构造器
- 实现与优化技巧
- 链式调用 vs 方法重载
- 如何避免过度设计?
原型模式(Prototype)
- 原型模式的工作原理是什么?
- 通过克隆已有对象来创建新对象,避免重复初始化步骤。
- 场景:深度复制、缓存对象
- 实现与注意事项
- 浅拷贝 vs 深拷贝:性能与内存消耗的权衡
- 如何处理循环引用?
第三阶段:结构型模式——如何灵活组合类和对象?
适配器模式(Adapter)
- 适配器模式的作用是什么?
- 将一个类的接口转换成客户端所期望的另一个接口。
- 场景:第三方库集成、旧代码重构
- 实现与变体
- 对象适配器 vs 类适配器
- 如何选择合适的适配器?
装饰器模式(Decorator)
- 装饰器模式是如何工作的?
- 动态地给对象添加一些额外的职责,而不改变其原始结构。
- 场景:日志记录、权限控制、缓存
- 实现与最佳实践
- 函数装饰器 vs 类装饰器
- 如何避免过度嵌套?
代理模式(Proxy)
- 代理模式的核心思想是什么?
- 为其他对象提供一种代理以控制对这个对象的访问。
- 场景:延迟加载、权限检查、远程代理
- 实现与类型
- 虚拟代理、保护代理、智能引用代理
- 如何选择合适的代理类型?
外观模式(Facade)
- 外观模式解决了什么问题?
- 为复杂的子系统提供一个统一的高层接口,使子系统更容易使用。
- 场景:框架封装、模块化系统集成
- 实现与注意事项
- 如何平衡简洁性与功能性?
- 如何防止“上帝对象”?
桥接模式(Bridge)
- 桥接模式的作用是什么?
- 将抽象部分与它的实现部分分离,使它们都可以独立地变化。
- 场景:跨平台图形库、设备驱动程序
- 实现与变体
- 接口与实现的解耦
- 如何避免过度设计?
第四阶段:行为型模式——如何合理分配职责?
观察者模式(Observer)
- 观察者模式的工作原理是什么?
- 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
- 场景:事件驱动架构、状态管理
- 实现与优化
- 发布/订阅模式 vs 观察者模式
- 如何避免内存泄漏?
策略模式(Strategy)
- 策略模式的核心思想是什么?
- 定义一系列算法,把它们一个个封装起来,并且使它们可以互相替换。
- 场景:排序算法、支付网关
- 实现与最佳实践
- 算法与上下文的分离
- 如何选择合适的策略?
模板方法模式(Template Method)
- 模板方法模式的作用是什么?
- 定义一个操作中的算法骨架,而将一些步骤延迟到子类中实现。
- 场景:框架设计、业务流程自动化
- 实现与注意事项
- 钩子函数的使用
- 如何避免过度继承?
命令模式(Command)
- 命令模式是如何工作的?
- 将请求封装成对象,从而使你可以用不同的请求对客户进行参数化。
- 场景:撤销/重做功能、任务队列
- 实现与优化
- 请求与执行的解耦
- 如何管理命令历史?
责任链模式(Chain of Responsibility)
- 责任链模式的作用是什么?
- 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合。
- 场景:中间件管道、审批流程
- 实现与变体
- 动态链 vs 静态链
- 如何避免链过长?
迭代器模式(Iterator)
- 迭代器模式的工作原理是什么?
- 提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。
- 场景:遍历集合、惰性求值
- 实现与优化
- 内部迭代器 vs 外部迭代器
- 如何处理异步迭代?
备忘录模式(Memento)
- 备忘录模式解决了什么问题?
- 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后恢复到原先保存的状态。
- 场景:撤销/重做功能、游戏存档
- 实现与注意事项
- 如何避免性能瓶颈?
- 如何处理复杂状态?
第五阶段:高级设计模式与应用实践
状态模式(State)
- 状态模式的核心思想是什么?
- 允许对象在其内部状态改变时改变它的行为,看起来好像改变了它的类。
- 场景:工作流引擎、用户登录状态管理
- 实现与最佳实践
- 状态机 vs 状态模式
- 如何避免状态爆炸?
解释器模式(Interpreter)
- 解释器模式的作用是什么?
- 给定一种语言,定义它的文法表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。
- 场景:DSL(领域特定语言)、查询解析器
- 实现与优化
- 递归下降解析器 vs LL(1) 解析器
- 如何提高解析效率?
中介者模式(Mediator)
- 中介者模式解决了什么问题?
- 用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
- 场景:聊天室、组件通信
- 实现与注意事项
- 如何设计高效的中介者?
- 如何避免中介者成为“上帝对象”?
访问者模式(Visitor)
- 访问者模式的工作原理是什么?
- 表示一个作用于某对象结构中的各元素的操作,它使你可以在不修改各元素类的前提下定义作用于这些元素的新操作。
- 场景:编译器、报表生成器
- 实现与优化
- 双分派 vs 单分派
- 如何避免双重分派带来的复杂性?
组合模式(Composite)
- 组合模式的作用是什么?
- 将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。
- 场景:文件系统、组织架构图
- 实现与注意事项
- 叶节点 vs 组合节点
- 如何处理非均匀层次结构?