设计模式汇总
设计模式大纲

设计原则:(SOLID原则)
简写 | 原则名 | 说明 |
---|---|---|
SRP | 单一职责原则 | 每个模块或类都应该对软件提供的功能的一部分负责,而这个责任应该完全由类来封装。它的所有服务都应严格遵守这一职责。 |
OCP | 开闭原则 | 软件中的对象(类、模块、函数等)对扩展是开放的,对修改是封闭的。 |
LSP | 里氏替换原则 | 所有使用基类的地方必须能透明地使用其子类的对象。 |
ISP | 接口隔离原则 | 客户端不应该依赖它不需要的接口。 |
DIP | 依赖倒转原则 | 是指一种特定的解耦(传统的依赖关系建立在高层次上,而具体的策略设置则应用在低层次的模块上)形式,使得高层次的模块不依赖于低层次的模块的实现细节,依赖关系被颠倒(反转),从而使得低层次模块依赖于高层次模块的需求抽象。 |
LoD/PoLK | 迪米特法则/最少知识原则 | 1. 每个对象应该对其他对象尽可能最少的知道 2. 每个对象应该仅和其朋友通信;不和陌生人通信 3. 仅仅和直接朋友通信 |
设计模式汇总
分类 | 设计模式 | 英文名 | 作用 | 使用场景 | 案例 |
---|---|---|---|---|---|
创建型 | 单例模式 | Singleton | 确保类仅有一个实例,并提供全局访问点 | 当需要全局访问且仅允许一个实例时 | 全局配置管理器、日志记录器 |
创建型 | 工厂模式 | Factory | 根据参数创建相关类型对象,解耦对象创建与使用 | 当对象创建逻辑复杂或需要根据参数动态创建对象时 | 数据库连接工厂、文件解析器工厂 |
创建型 | 建造者模式 | Builder | 通过设置可选参数构建复杂对象,定制化创建过程 | 当需要构建复杂对象且对象的构建过程复杂时 | 构建复杂的产品对象,如汽车配置器 |
创建型 | 原型模式 | Prototype | 复制现有对象创建新对象,适用于创建成本高且对象差异小的场景 | 当对象创建成本高且需要频繁创建相似对象时 | 游戏中的克隆角色、文档编辑中的复制粘贴 |
结构型 | 代理模式 | Proxy | 引入代理类控制对原类的访问,可添加额外处理 | 当需要控制对某个对象的访问或添加额外逻辑时 | 远程代理、缓存代理、权限代理 |
结构型 | 桥接模式 | Bridge | 分离抽象与实现,使两者能独立变化,降低耦合度 | 当需要分离抽象与实现,使两者独立变化时 | UI框架中分离视图与实现逻辑 |
结构型 | 装饰器模式 | Decorator | 以组合方式动态为对象添加额外功能 | 当需要动态为对象添加功能且不想修改原有类时 | 动态添加功能,如文本编辑器中的字体加粗、斜体 |
结构型 | 适配器模式 | Adapter | 将不兼容接口转换为可兼容接口,使类协同工作 | 当需要让不兼容的类协同工作时 | 将旧系统接口适配到新系统中 |
结构型 | 门面模式 | Facade | 提供简化接口,用于访问子系统的一组接口 | 当需要隐藏子系统的复杂性,提供简单接口时 | 外部API调用,如电商系统中的支付接口 |
结构型 | 组合模式 | Composite | 将对象组织成树形结构,统一处理单个与组合对象 | 当需要表示对象的层次结构时 | 文件系统中的文件和文件夹管理 |
结构型 | 享元模式 | Flyweight | 通过共享对象减少内存消耗,用于大量重复对象场景 | 当需要减少重复对象的内存占用时 | 游戏中的子弹对象、文本编辑器中的字符对象 |
行为型 | 观察者模式 | Observer | 解耦观察者与被观察者,实现事件发布 - 订阅机制 | 当需要实现事件驱动或对象间依赖关系时 | GUI事件处理、股票行情更新 |
行为型 | 模板方法模式 | Template Method | 定义算法骨架,由子类实现特定步骤 | 当需要定义固定流程但某些步骤需要子类实现时 | 算法框架、游戏关卡流程 |
行为型 | 策略模式 | Strategy | 封装算法,使其可互相替换,解耦算法与使用代码 | 当需要在运行时动态切换算法时 | 排序算法切换、支付方式切换 |
行为型 | 职责链模式 | Chain of Responsibility | 多个处理器顺序处理请求,可动态传递请求 | 当需要多个对象顺序处理请求且处理顺序不确定时 | 客户端请求处理、异常处理链 |
行为型 | 迭代器模式 | Iterator | 遍历集合对象,解耦容器与遍历代码 | 当需要遍历集合但不想暴露内部结构时 | 集合框架中的遍历操作 |
行为型 | 状态模式 | State | 实现状态机,根据状态变化执行不同行为 | 当对象的行为依赖于其状态且状态变化复杂时 | 游戏角色状态(正常、受伤、死亡)、订单状态管理 |
行为型 | 访问者模式 | Visitor | 解耦操作与对象,对对象结构中的对象执行操作 | 当需要对对象结构中的对象执行多种操作且不想修改对象结构时 | 报表生成、代码分析工具 |
行为型 | 备忘录模式 | Memento | 在不破坏封装的情况下捕获并保存对象状态以便恢复 | 当需要保存和恢复对象状态时 | 游戏存档、文档撤销操作 |
行为型 | 命令模式 | Command | 将请求封装成对象,支持请求排队、日志记录与撤销操作 | 当需要将操作封装为对象以便后续处理时 | 撤销操作、命令队列 |
行为型 | 解释器模式 | Interpreter | 通过定义语法规则实现简单语言的解释器 | 当需要解析和执行简单语言时 | 简单的表达式求值、小型脚本语言解析 |
行为型 | 中介模式 | Mediator | 引入中介层简化多个对象间复杂交互,转换为一对多关系 | 当多个对象之间交互复杂且需要集中管理时 | 聊天室、交通控制系统 |
设计模式分类:
创建型设计模式
创建型设计模式包括:单例模式、工厂模式、建造者模式、原型模式。 它主要解决:对象的创建问题,封装复杂的创建过程,解耦对象的创建代码和使用代码。
1. 单例模式Singleton
使用说明
- 单例模式确保一个类只有一个实例,并提供一个全局访问点。
使用场景
- 需要严格控制实例数量的场景,如配置管理器、连接池等。
- 全局访问点,如日志记录器。
实现方式
- 饿汉式:类加载时就初始化实例。
- 懒汉式:首次使用时初始化实例。
- 双重检测:多线程环境下的懒汉式实现。
- 静态内部类:利用类加载机制实现延迟加载。
- 枚举:通过枚举实现单例,简单且线程安全。
模式优点
- 减少内存消耗,节省系统资源。
- 控制资源访问,实现集中管理。
模式缺点
- 违反了类的单一职责原则,增加类的功能复杂度。
- 难以测试,因为单例实例全局可访问,测试时难以控制。
- 扩展困难,单例模式限制了实例的扩展。
使用该模式的标准
- 当类必须只有一个实例时使用。
- 当这个唯一实例的多个客户端需要共享资源时使用。
补充说明
- 虽然单例模式有其缺点,但在某些场景下,如全局配置管理,它仍然是一个有效的解决方案。替代方案包括工厂模式和依赖注入(IOC)容器,它们可以提供更灵活的实例管理。
golang
- sync.Once
2. 工厂模式Factory
使用说明
- 工厂模式用于创建相关类型的对象,通过参数决定具体创建哪种类型的对象。它包含简单工厂、工厂方法和抽象工厂三种形式,其中简单工厂和工厂方法较为常用。
使用场景
- 简单工厂模式:适用于对象创建逻辑简单,多个对象创建逻辑可以集中管理的情况。
- 工厂方法模式:适用于对象创建逻辑复杂,需要将创建逻辑分散以避免工厂类过于庞大。
- 抽象工厂模式:适用于需要创建一系列相关或依赖对象的场景,使用较少。
判断是否使用该模式的标准:
- 封装变化:当对象创建逻辑可能变化时,使用工厂模式可以透明化这些变化。
- 代码复用:通过工厂类复用创建代码。
- 隔离复杂性:封装复杂的创建逻辑,简化调用者代码。
- 控制复杂度:减少函数或类的职责,使代码更简洁。
补充说明
- 工厂模式在依赖注入框架中应用广泛,如Spring IOC和Google Guice,它们通过集中管理对象的创建、组装和管理,实现与业务代码的解耦,帮助开发者专注于业务逻辑的开发。
使用说明
- 工厂模式通过参数化方式创建对象,适用于简单工厂、工厂方法和抽象工厂三种场景。
使用场景
- 简单工厂:适用于简单的对象创建逻辑,便于集中管理。
- 工厂方法:适用于复杂的对象创建逻辑,避免工厂类过于庞大。
- 抽象工厂:适用于创建一系列相关或依赖对象的场景。
判断是否使用该模式的标准:
- 封装变化:对象创建逻辑可能变化时,使用工厂模式。
- 代码复用:通过工厂类复用创建代码。
- 隔离复杂性:封装复杂的创建逻辑。
补充说明
- 工厂模式在依赖注入框架中广泛应用,如Spring IOC和Google Guice,实现对象创建与管理的解耦。
golang 举例
- sync.Once
3. 建造者模式Builder
使用说明
- 建造者模式用于构建复杂对象,允许通过设置可选参数来定制化创建对象。
使用场景
- 类属性众多,构造函数参数列表过长。
- 类的属性之间存在依赖关系或约束条件。
- 需要创建不可变对象,对象创建后不可修改属性。
实现方式
- 通过建造者类暴露设置属性的方法,逐步构建复杂对象。
- 建造者类包含一个内部产品类,最终提供一个获取产品对象的方法。
模式优点
- 易于扩展和维护,新增属性或修改构建过程简单。
- 客户端代码不需要知道对象的构建细节。
模式缺点
- 可能会产生多余的建造者类,增加系统复杂度。
补充说明
- 建造者模式适用于对象的构建过程需要灵活性和可定制性的场景,同时保持客户端代码的简洁性。
golang 举例
- 对比函数选项模式
4. 原型模式Prototype
使用说明 原型模式通过复制现有对象(原型)来创建新对象,适用于对象创建成本高且对象间差异小的场景。
使用场景
- 创建成本高的对象。
- 需要快速复制对象以节省时间。
- 对象间大部分字段相同,差异小。
实现方式
- 浅拷贝:复制基本数据类型和引用对象的地址。
- 深拷贝:完全独立复制对象,包括引用对象及其引用对象。
模式优点
- 减少对象创建时间,提高效率。
- 可以复制复杂对象,无需重新初始化。
模式缺点
- 深拷贝消耗更多时间和内存。
- 浅拷贝可能导致对象共享数据,增加数据被意外修改的风险。
补充说明
- 在性能和资源消耗之间需要权衡,选择合适的拷贝方式(浅拷贝或深拷贝)。对于不可变对象,浅拷贝是安全的;对于可变对象,需谨慎使用浅拷贝。
golang 举例
- sync.pool
结构型设计模式
结构型模式包括:代理模式、桥接模式、装饰器模式、适配器模式、门面模式、组合模式、享元模式 结构型设计模式主要解决:“类或对象的组合”问题。它总结了一些类或对象组合在一起的经典结构,这些经典的结构可以解决特定应用场景的问题。
1. 代理模式Proxy
使用说明 代理模式通过引入代理类来控制对原类的访问,代理类与原类具有相同的接口,适合对基础库的再封装。
使用场景
- 需要控制对原类对象的访问时。
- 需要在访问原类对象时添加额外的处理,如日志、权限检查等。
实现方式
- 静态代理:为每个需要代理的类编写一个代理类。
- 动态代理:在运行时动态创建代理类,使用反射机制。
模式优点
- 增加了对象的访问控制,提高了系统的灵活性。
- 能够将业务逻辑与非业务逻辑(如日志、权限检查)解耦。
模式缺点
- 静态代理增加了代码量,每个类都需要一个代理类。
- 动态代理可能引入性能开销。
补充说明 代理模式在RPC、缓存等场景中广泛应用,有助于实现业务功能与非业务功能的分离。
golang 举例
- XXXProxy
2. 桥接模式Bridge
使用说明 桥接模式将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
使用场景
- 需要降低抽象和实现之间的耦合度。
- 需要对抽象或实现进行扩展,而不影响另一部分。
实现方式
- 通过抽象类与实现类的组合关系,而非继承关系,实现解耦。
模式优点
- 抽象和实现可以独立扩展和维护。
- 减少了继承层次,简化系统结构。
模式缺点
- 增加了系统的复杂性,需要理解组合关系。
- 实现可能不如继承那样直接和简单。
补充说明
- 桥接模式适用于需要对抽象和实现进行独立扩展的场景,有助于减少代码的耦合度,但在实际项目中使用频率不高。
3. 装饰器模式Decorator
使用说明 装饰器模式通过组合而非继承的方式,动态地给对象添加额外功能、职责。
使用场景
- 需要动态地扩展对象功能。
- 需要透明地扩展多个功能。
实现方式
- 创建一个与原始类相同的抽象类或接口。
- 创建具体装饰器类,实现该抽象类或接口,并持有原始类的引用。
- 装饰器类可以嵌套使用,以实现多层功能的叠加。
模式优点
- 动态扩展功能,无需修改原始类代码。
- 支持多个装饰器的嵌套使用。
模式缺点
- 可能会导致系统更加复杂,增加理解难度。
- 多层装饰器嵌套可能导致性能问题。
补充说明
- 装饰器模式适用于需要灵活扩展功能的场景,但需注意不要过度使用,以免系统变得难以维护。
4. 适配器模式Adapter
使用说明 适配器模式用于将不兼容的接口转换为可兼容的接口,使原本不能一起工作的类可以协同工作。分为类适配器(继承)和对象适配器(组合)两种形式。
使用场景
- 封装有缺陷的接口设计。
- 统一多个类的接口设计。
- 替换依赖的外部系统。
- 兼容老版本接口。
- 适配不同格式的数据。
实现方式
- 类适配器:通过多重继承实现,将适配的类作为父类。
- 对象适配器:通过组合实现,将适配的类作为成员变量。
模式优点
- 解决接口不兼容问题,提高系统的灵活性。
- 避免修改原有代码,实现兼容。
模式缺点
- 过度使用可能导致系统结构复杂。
- 可能隐藏接口之间的不一致性。
补充说明 适配器模式是补救策略,用于处理设计上的缺陷。在设计初期应尽量避免接口不兼容问题,减少适配器模式的使用。
5. 门面模式Facade
使用说明 门面模式提供一个简化的接口,用于访问子系统中的一组接口。
使用场景
- 子系统接口复杂,需要简化访问时。
- 需要解决性能问题或分布式事务管理。
实现方式
- 创建一个门面类,该类将客户端请求委托给子系统中的适当对象。
模式优点
- 简化客户端接口,隐藏子系统复杂性。
- 提高客户端和子系统之间的耦合灵活性。
模式缺点
- 可能会隐藏客户端对子系统的理解,过度使用可能导致系统难以维护。
补充说明 门面模式适用于需要简化复杂子系统接口的场景,但需注意不要过度使用,以免系统变得难以维护。
6. 组合模式Composite
使用说明 组合模式用于处理树形结构数据,将对象组织成树形结构,统一处理单个对象和组合对象。
使用场景
- 数据自然形成树形结构的场景。
- 需要递归遍历操作树形结构的场景。
实现方式
- 创建抽象类,定义对象和组合对象的共有接口。
- 叶子对象和组合对象都实现这个抽象类。
- 通过递归算法处理树形结构中的每个节点。
模式优点
- 简化树形结构的代码实现。
- 统一处理对象和组合对象。
模式缺点
- 应用场景受限,仅限于树形结构数据。
- 可能导致系统结构复杂,难以理解。
补充说明 组合模式适用于需要操作树形结构的场景,通过递归简化实现,但需注意其应用范围的局限性。
7. 享元模式Flyweight
使用说明 享元模式通过共享对象减少内存消耗,适用于大量重复对象的场景。
使用场景
- 系统中存在大量重复对象。
- 对象创建成本高,需要节省内存。
实现方式
- 将对象设计为享元,共享内存中的实例。
- 提取相似对象的共同部分作为享元。
模式优点
- 减少内存消耗,提高系统性能。
模式缺点
- 需要设计合适的享元对象,增加设计复杂度。
- 享元对象的状态必须是不可变的,限制了使用场景。
补充说明 享元模式适用于对象复用的场景,但需注意享元对象的不可变性,以避免共享对象间的相互影响。
行为型设计模式
行为型模式比较多,分别是:观察者模式、模板模式、策略模式、职责链模式、迭代器模式、状态模式、访问者模式、备忘录模式、命令模式、解释器模式、中介模式。 行为型设计模式主要解决:“类或对象之间的交互”问题
1. 观察者模式Observe
使用说明 观察者模式通过解耦观察者和被观察者,实现事件的发布-订阅机制。
使用场景
- 需要事件通知机制的场景,如邮件订阅、RSS Feeds。
- 需要系统组件间低耦合的场景。
实现方式
- 同步阻塞:经典实现,适用于代码解耦。
- 异步非阻塞:提高执行效率,适用于性能要求高的场景。
- 跨进程:基于消息队列,适用于不同进程间的交互。
模式优点
- 降低系统组件间的耦合度。
- 提高代码的可维护性和可扩展性。
模式缺点
- 实现复杂,尤其是跨进程通信。
- 可能引入性能问题,如异步处理中的回调地狱。
补充说明 EventBus框架提供了观察者模式的实现骨架,简化了在业务场景中的应用,无需从零开发。
2. 模板方法模式Template
使用说明 模板方法模式定义算法骨架,让子类实现特定步骤,保持算法结构不变。
使用场景
- 需要在子类中复用算法框架时。
- 需要在不修改算法结构的情况下扩展功能时。
实现方式
- 定义一个抽象类,其中包含模板方法和算法的骨架。
- 子类继承抽象类并实现特定的算法步骤。
模式优点
- 代码复用,减少重复代码。
- 提供扩展点,便于功能定制。
模式缺点
- 可能导致类体系结构变得复杂。
补充说明 模板模式通过继承实现代码复用和扩展,适用于需要在不同子类中复用相同算法结构的场景。
3. 策略模式Strategy
使用说明 策略模式通过定义一组算法类,将算法封装并允许互相替换,实现算法与使用算法的代码之间的解耦。
使用场景
- 需要替换或选择不同算法的场景。
- 避免使用冗长的 if-else 或 switch 分支判断。
- 提供框架扩展点,满足开闭原则。
实现方式
- 定义策略接口和实现该接口的策略类。
- 使用工厂类封装策略创建的细节。
- 客户端代码在编译时或运行时选择使用的具体策略。
模式优点
- 算法变化不影响客户端代码,实现解耦。
- 降低代码复杂度,易于管理和扩展。
- 支持开闭原则,方便添加新策略。
模式缺点
- 可能会引入过多策略类,增加系统复杂度。
- 运行时动态确定策略可能影响性能。
4. 职责链模式Chain of Responsibility
使用说明 职责链模式通过多个处理器顺序处理请求,每个处理器完成自己的职责后可将请求传递给下一个处理器。
使用场景
- 需要多个对象依次处理请求的场景。
- 需要实现过滤器或拦截器功能。
实现方式
- 定义处理器接口,包含处理请求的方法。
- 每个具体的处理器类实现该接口,并决定是否将请求传递给下一个处理器。
模式优点
- 降低对象之间的耦合度。
- 增强系统的可扩展性,可动态添加或删除处理器。
模式缺点
- 可能导致请求处理的流程变得复杂,难以跟踪和调试。
- 性能问题,如果链过长,可能导致处理延迟。
补充说明 职责链模式适用于请求处理者不固定的场景,通过链式结构实现请求的动态分发。
5. 迭代器模式Iterator
使用说明 迭代器模式用于遍历集合对象,解耦容器与遍历代码。
使用场景
- 需要遍历不同数据结构,如数组、链表、树等。
- 需要统一遍历接口,简化集合操作。
实现方式
- 提供统一的迭代器接口。
- 为每种集合结构实现该迭代器接口。
模式优点
- 封装遍历逻辑,简化集合操作。
- 支持多种遍历算法,易于扩展。
模式缺点
- 需要为每种集合实现迭代器,增加工作量。
- 迭代过程中修改集合可能导致问题。
补充说明 迭代器模式通过提供统一的遍历接口,简化了集合的遍历操作,但需注意迭代过程中集合修改的问题。
6. 状态模式State
使用说明 状态模式用于实现状态机,适用于需要根据状态变化执行不同行为的场景。
使用场景
- 游戏开发中角色状态变化。
- 工作流引擎中任务状态管理。
实现方式
- 分支逻辑法:使用 if-else 或 switch-case 语句实现状态转移。
- 查表法:使用二维数组表示状态转移图,提高代码可读性。
- 状态模式:适用于状态和转移简单,但业务逻辑复杂的情况。
模式优点
- 将状态具体化为对象,易于管理和扩展。
- 封装状态相关的操作,减少状态转移错误。
模式缺点
- 状态较多时,可能导致系统复杂度增加。
补充说明 状态模式通过将状态封装为对象,简化了状态管理,适用于状态变化频繁且需要执行特定动作的场景。
7. 访问者模式Visitor
使用说明 访问者模式使操作与对象解耦,适用于需要对对象结构中的对象执行操作的场景。
使用场景
- 需要对对象结构中的对象执行多个操作。
- 需要在不修改对象结构的情况下扩展新操作。
实现方式
- 定义访问者接口和具体访问者类。
- 对象结构中的每个元素类实现接受访问者的方法。
模式优点
- 扩展新操作容易,无需修改对象结构。
- 操作集中管理,不分散在各个类中。
模式缺点
- 增加新元素类时,需要在访问者接口中增加新方法,违反开闭原则。
- 客户端代码与访问者模式耦合,可读性差。
补充说明 访问者模式适用于操作复杂且需要频繁变化的场景,但需注意其对元素类和访问者类的扩展限制。
8. 备忘录模式Memento
使用说明 备忘录模式用于在不破坏封装的情况下,捕获并保存对象状态以便将来恢复。
使用场景
- 需要实现撤销/恢复功能。
- 防止数据丢失。
实现方式
- 通过备忘录对象存储原对象状态。
- 通过发起人对象请求恢复状态。
模式优点
- 支持内部状态的捕获和恢复。
- 保持封装性,外部无法直接访问对象状态。
模式缺点
- 存储大量对象状态可能导致资源消耗。
- 恢复操作可能影响性能。
补充说明 备忘录模式适用于需要撤销操作的场景,需权衡存储和性能开销。
9. 命令模式
使用说明 命令模式将请求封装成对象,允许使用请求排队、记录请求日志、撤销操作等。
使用场景
- 需要异步、延迟或排队执行命令。
- 需要撤销和重做操作。
- 需要记录操作日志。
实现方式
- 定义命令接口和具体命令类。
- 创建调用者对象,接收命令并执行。
- 命令对象包含执行操作的方法。
模式优点
- 降低系统组件间的耦合度。
- 提供灵活的命令管理,如排队、记录日志等。
模式缺点
- 可能会引入过多命令类,增加系统复杂度。
- 运行时动态确定命令可能影响性能。
10. 解释器模式
使用说明 解释器模式用于实现一个简单的语言解释器,通过定义语言的语法规则来解析和执行表达式。
使用场景
- 需要解释特定领域语言的场景。
- 需要实现脚本语言解析的场景。
实现方式
- 将语法规则拆分成多个小类,每个类负责一部分解析工作。
- 通过组合这些小类来实现整个语法规则的解析。
模式优点
- 提供了一种灵活的方式来扩展和修改语言的语法。
- 降低了语言解析的复杂性,易于管理和维护。
模式缺点
- 对于复杂语言,可能导致类的数量过多,系统变得复杂。
- 性能可能不如专用的编译器或解释器。
补充说明 解释器模式适用于需要快速实现简单语言解析的场景,但需注意其在处理复杂语言时可能带来的性能和复杂性问题。
11. 中介模式Meiator
使用说明 中介模式通过引入中介层简化多个对象间的复杂交互,将多对多关系转换为一对多关系。
使用场景
- 需要简化复杂对象间的交互关系。
- 对象间存在多对多的交互,需要降低耦合度。
实现方式
- 创建中介类,作为各个对象交互的中心节点。
- 对象通过中介类进行通信,而不是直接交互。
模式优点
- 减少对象间的直接交互,降低系统复杂度。
- 提高代码的可读性和可维护性。
模式缺点
- 可能过度集中控制,导致中介类变得复杂。
- 系统对中介类的依赖性增强,可能影响性能。
补充说明
- 中介模式适用于对象间交互复杂且需要解耦的场景,需注意中介类的合理设计,避免过度集中。