type
status
date
slug
summary
tags
category
password
notion image
xmind.aixmind.ai设计模式.xmind | Xmind AI

1、设计原则

1.1 面向对象设计的经典原则

软件设计原则是指导开发者编写高质量、可维护、可扩展代码的基本准则。以下是软件设计中常用的软件设计原则,包括最经典的面向对象编程的 SOLID 原则
  • SOLID 原则:
    • 单一职责原则(Single Responsibility Principle)一个类的职责应该单一,不要承担过多的职责。单一职责适用于接口、类和方法。
    • 开闭原则(Open/Closed Principle):“开”指的是对扩展开放,而“闭”则指的是对修改关闭。简单来讲就是不要修改已有的代码,而要去编写新的代码。开闭原则的核心是通过高层抽象的泛化来保证底层实现的多态化扩展,这样就不需要对现有的系统进行反复修改。
    • 里氏替换原则(Liskov Substitution Principle)所有父类能出现的地方,子类就可以出现,并且替换了也不会出现任何错误。这就要求子类的所有相同方法,都必须遵循父类的约定,否则当父类替换为子类时就会出错。面向对象设计语言的特性“继承与多态”正是为此而生。
    • 接口隔离原则(Interface Segregation Principle)接口的内容一定要尽可能地小,能有多小就多小。所以切勿将接口定义成全能型的,否则实现类就必须神通广大,这样便丧失了子类实现的灵活性。
    • 依赖倒置原则(Dependency Inversion Principle):指高层模块不依赖底层模块,也就是说高层模块只依赖上层抽象,而不直接依赖具体的底层实现,从而达到降低耦合的目的。例如面向接口编程。通过抽象成接口,使各个类的实现彼此独立,实现类之间的松耦合。
  • 迪米特法则(law of Demeter):又叫作最少知识原则(The Least Knowledge Principle),通俗的来讲,就是一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类来说,无论逻辑多么复杂,都尽量地的将逻辑封装在类的内部,对外除了提供的 public 方法,不对外泄漏任何信息。设计模式中的门面模式(Facade)和中介模式(Mediator)都是迪米特法则的应用的例子。
  • 合成复用原则(Composition Over Inheritance):合成复用原则是指尽量使用组合/聚合,而不是使用继承。只有当以下的条件全部被满足时,才应当使用继承关系:
    • 子类是超类的一个特殊种类,而不是超类的一个角色,也就是区分“Has-A”和“Is-A”.只有“Is-A”关系才符合继承关系,“Has-A”关系应当使用聚合来描述。
    • 永远不会出现需要将子类换成另外一个类的子类的情况。如果不能肯定将来是否会变成另外一个子类的话,就不要使用继承。
    • 子类具有扩展超类的责任,而不是具有置换掉或注销掉超类的责任。如果一个子类需要大量的置换掉超类的行为,那么这个类就不应该是这个超类的子类。

1.2 一句话归纳七大设计原则

设计原则
一句话归纳
目的
适用场景
单一职责原则(SRP)(Simple Responsibility)
一个类只干一件事
便于理解,提高代码可读性
类设计
开闭原则(OCP)(Open-Close)
对扩展开放,对修改关闭
减少维护带来新的风险
系统扩展
里氏替换原则(LSP) (Liskov Substitution)
子类可替换父类(继承与多态)
防止继承泛滥
继承设计
接口隔离原则(ISP)(Interface Segregation)
接口尽可能小而专
功能解耦,高聚合、低耦合
接口设计
依赖倒置原则(DIP)(Dependence Inversion)
依赖抽象而非实现(面向接口编程)
更利于代码结构的升级扩展
依赖管理
迪米特法则(LoD)(Law of Demeter)
对其他模块知道的越少越好
只和朋友交流,不和陌生人说话,减少代码臃肿
方法设计
合成复用原则(CARP)(Composite/Aggregate Reuse)
尽量使用组合实现代码复用,而不使用继承
降低代码耦合
依赖管理
SOLID 设计原则之间的关系:
  • 开闭原则是设计的终极目标,满足该原则可以达到最大限度的复用性和可维护性。当你的代码满足其他原则时,那么基本上就是满足开闭原则的。
  • 单一职责是所有设计原则的基础。
  • 里氏替换原则强调的是子类替换父类后程序运行时的正确性,它用来帮助实现开闭原则。
  • 而接口隔离原则用来帮助实现里氏替换原则,同时它也体现了单一职责。
  • 依赖倒置原则是过程式编程与面向对象编程的分水岭,同时它也被用来指导接口隔离原则。
notion image
简单地说:
  • 依赖倒置原则告诉我们要面向接口编程。
  • 当我们面向接口编程之后,接口隔离原则和单一职责原则又告诉我们要注意职责的划分,不要什么东西都塞在一起。
  • 当我们职责捋得差不多的时候,里氏替换原则告诉我们在使用继承的时候,要注意遵守父类的约定。
  • 上面说的这四个原则,它们的最终目标都是为了实现开闭原则。

2、设计模式

设计原则(如 SOLID)是指导思想,而设计模式基于设计原则实现的具体解决方案。设计模式是软件设计中常见问题的可重用解决方案,它们代表了最佳实践,是开发者长期经验的总结。但是在使用的过程中千万不要死记硬背,生搬硬套

2.1 GoF 23种设计模式

23 种设计模式分为三大分类:
类型
核心思想
模式
创建型
如何更灵活地创建对象
单例、工厂方法、抽象工厂、建造者、原型
结构型
如何组合类和对象形成更大结构
适配器、桥接、组合、装饰器、外观、享元、代理
行为型
如何更好地协作和分配职责
责任链、命令、解释器、迭代器、中介者、备忘录、观察者、状态、策略、模板方法、访问者

2.1.1 创建型模式(5种)

关注点:如何创建对象,将对象的创建与使用分离。
  1. 单例模式 (Singleton Pattern)
      • 意图: 保证一个类在整个程序运行期间只有有一个实例,并提供全局访问点用于获取这个实例。
      • 关键点: 私有构造函数、静态私有实例、静态公有获取方法。
      • 应用场景: 线程池、缓存、日志对象、对话框、注册表设置等需要全局唯一对象的场景。
  1. 工厂方法模式 (Factory Method Pattern)
      • 意图: 定义一个用于创建对象的接口,但让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
      • 关键点: 有一个抽象的“工厂”类和抽象的“产品”类,由具体工厂创建具体产品。
      • 应用场景: 日志记录器、数据库访问等,当需要屏蔽创建对象的复杂过程时。
  1. 抽象工厂模式 (Abstract Factory Pattern)
      • 意图: 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
      • 关键点: 包含多个工厂方法,每个方法负责创建一个“产品族”中的不同产品。
      • 应用场景: 生成不同风格的整个UI套件(如Windows或Mac风格的按钮、文本框);支持多种数据库的系统。
  1. 建造者模式 (Builder Pattern)
      • 意图: 将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。
      • 关键点: 将构造步骤抽象出来,由一个“导演”指导“建造者”一步步构建,最终返回完整对象。
      • 应用场景: 创建复杂的复合对象,如一份有很多可选配件的套餐、一封复杂的邮件(收件人、标题、正文、附件等)。
  1. 原型模式 (Prototype Pattern)
      • 意图: 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
      • 关键点: 实现 Cloneable 接口,重写 clone() 方法。
      • 应用场景: 当直接创建对象的成本比较大时(如需要繁琐的数据库操作、计算),通过复制一个现有对象来高效地创建新对象。

2.1.2 结构型模式(7种)

关注点:如何将类或对象按某种布局组成更大的结构。
  1. 适配器模式 (Adapter Pattern)
      • 意图: 将一个类的接口转换成客户希望的另外一个接口。使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
      • 关键点: 作为两个不兼容接口之间的桥梁。有类适配器(通过继承)和对象适配器(通过组合)两种。
      • 应用场景: 使用旧的类库、整合第三方SDK。
  1. 装饰器模式 (Decorator Pattern)
      • 意图: 动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。
      • 关键点: 装饰器和被装饰对象实现相同的接口,装饰器内部持有该接口的实例。
      • 应用场景: Java I/O流 (BufferedReaderLineNumberReader)、为图形界面组件添加滚动条、边框等功能。
  1. 代理模式 (Proxy Pattern)
      • 意图: 为其他对象提供一种代理以控制对这个对象的访问。
      • 关键点: 代理类和被代理类实现同一接口,代理类控制对被代理对象的访问。
      • 应用场景:远程代理(RMI、微服务调用)、均衡负载代理(Nginx)
  1. 外观模式 (Facade Pattern)
      • 意图: 为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
      • 关键点: 提供一个简单的入口,隐藏子系统的复杂性。
      • 应用场景: 简化类库的调用、封装复杂的底层操作。
  1. 组合模式 (Composite Pattern)
      • 意图: 将对象组合成树形结构以表示“部分-整体”的层次结构。使得用户对单个对象和组合对象的使用具有一致性。
      • 关键点: 树枝和叶子实现同一接口,树枝内部包含该接口的集合。
      • 应用场景: 文件系统(文件-文件夹)、公司组织架构(员工-部门)、UI窗口中的组件。
  1. 享元模式 (Flyweight Pattern)
      • 意图: 运用共享技术有效地支持大量细粒度的对象。
      • 关键点: 将对象的内在状态(可共享)和外在状态(由客户端指定)分离。
      • 应用场景: 文本编辑器中的字符对象、游戏中的大量树木或子弹。
  1. 桥接模式 (Bridge Pattern)
      • 意图: 将抽象部分与它的实现部分分离,使它们都可以独立地变化。
      • 关键点: 使用组合代替继承,避免类爆炸(如形状和颜色的组合)。
      • 应用场景: 图形和渲染引擎、不同平台上的UI组件。

2.1.3 行为型模式(11种)

关注点:对象之间的职责分配和通信。
  1. 策略模式 (Strategy Pattern)
      • 意图: 定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
      • 关键点: 定义策略接口,不同的算法实现该接口。上下文类持有一个策略对象的引用。
      • 应用场景: 支付方式选择(支付宝、微信、信用卡)、排序算法选择、促销折扣计算。
  1. 模板方法模式 (Template Method Pattern)
      • 意图: 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
      • 关键点: 在抽象类中定义算法骨架和抽象方法,子类实现具体步骤。
      • 应用场景: Java AQS框架、Servlet的doGet/doPost方法、代码复用。
  1. 责任链模式 (Chain of Responsibility Pattern)
      • 意图: 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
      • 关键点: 每个处理器都持有下一个处理器的引用。
      • 应用场景: 审批流程、异常处理、Java Web中的过滤器 (FilterChain)。
  1. 观察者模式 (Observer Pattern)
      • 意图: 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
      • 关键点: 主题(被观察者)维护一个观察者列表,状态改变时通知它们。
      • 应用场景: 事件驱动系统、MVC模式中的模型-视图关系、消息订阅、React/Vue的响应式原理。
  1. 命令模式 (Command Pattern)
      • 意图: 将一个请求封装为一个对象,从而使您可以用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。
      • 关键点: 将“请求”抽象成对象,分离命令的发起者和执行者。
      • 应用场景: 图形界面中的菜单命令、事务操作、可撤销功能、宏命令。
  1. 解释器模式 (Interpreter Pattern)
      • 意图: 给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
      • 关键点: 为简单的语言文法定义类层次结构。
      • 应用场景: SQL解析、正则表达式引擎、数学表达式计算器。(使用较少)
  1. 迭代器模式 (Iterator Pattern)
      • 意图: 提供一种方法顺序访问一个聚合对象中各个元素,而又无须暴露该对象的内部表示。
      • 关键点: 将集合的遍历行为抽象到迭代器对象中。
      • 应用场景: Java集合框架中的 Iterator
  1. 中介者模式 (Mediator Pattern)
      • 意图: 用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
      • 关键点: 将复杂的网状结构变为星型结构,中介者是通信的中心。
      • 应用场景: 图形用户界面中多个组件的交互(如按钮、列表框的联动)、聊天室。
  1. 备忘录模式 (Memento Pattern)
      • 意图: 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。
      • 关键点: 原发器生成备忘录,管理者保存备忘录。
      • 应用场景: 游戏存档、文本编辑器的撤销/恢复、事务回滚。
  1. 状态模式 (State Pattern)
      • 意图: 允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。
      • 关键点: 将不同状态的行为分离到不同的状态类中,上下文对象将行为委托给当前状态对象。
      • 应用场景: 工作流引擎、游戏中的角色状态(站立、行走、跳跃)、电梯的运行状态。
  1. 访问者模式 (Visitor Pattern)
      • 意图: 主要将数据结构与数据操作分离。表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
      • 关键点: 通过“双分派”机制,在元素类中提供一个接受访问者的方法。
      • 应用场景: 编译器语法树分析、XML文档处理、复杂的对象结构遍历。(结构复杂,使用需谨慎)

2.2 一句话归纳23种设计模式

设计模式
一句话归纳
目的
生活案例
框架源码举例
原型模式(Prototype )
拔一根猴毛,吹出干万个
高效创建对象
克隆
ArrayList、PrototypeBean
建造者模式(Builder)
高配中配与低配,想选哪配就哪配
开放个性配置步骤
选配
StringBuilder、BeanDefinitionBuilder
代理模式(Proxy)
没有资源没时间, 得找媒婆来帮忙
增强职责
媒婆
ProxyFactoryBean、JdkDynamicAopProxy、CglibAopProxy
门面模式(Facade)
打开一扇门,走向全世界
统一访问入口
前台
JdbcUtils、RequestFacade
装饰器模式(Decorator)
他大舅他二舅,都是他舅
灵活扩展、同宗同源
煎饼
BufferedReader、Inputstream
适配器模式(Adapter)
适合自己的,才是最好的
兼容转换
电源造配
AdvisorAdapten、HandlerAdapter
桥接模式(Bridge)
约定优于配置
不允许用继承
DriverManager
委派模式(Delegate)
这个需求很简单,怎么实现我不管
只对结果负责
授权委托书
ClassLoader、 BeanDefinitionParserDelegate
模板模式(Template)
流程全部标准化,需要微调请覆盖
逻辑复用
把大象装进冰箱
JdbcTemplate、HttpServlet
策略模式(Strategy)
条条大道通北京,具体哪条你来定
把选择权交给用户
选择支付方式
Comparator、 Instantiationstrategy
责任链模式(Chain of Responsibility)
各人自扫门前雪,莫管他人瓦上霜
解耦处理逻辑
踢皮球
FilterChain、Pipeline
迭代器模式(Iterator)
流水线上坐一天,每个包裹扫
统一对集合的访问
逐个检票进站
Iterator
命令模式(Command)
运筹帷幄之中,决胜干里之外
解耦请求和处理
遥控器
Runnable、TestCase
状态模式(State)
状态驱动行为,行为决定状态
绑定状态和行为
订单状态跟踪
Lifecycle
备忘录(Memento)
给我一剂"后悔药“
备份
草槁箱
StateManageableMessageContext
中介者(Mediator)
联系方式我给你,怎么搞定我不管
统一管理网状资源
朋友圈
Timer
解释器模式(Interpreter)
我想说"方言",一切解释权归我所有
实现特定语法解析
摩斯密码
Pattern、ExpressionParser
观察者模式(Observer)
到点就通知我
解耦观察者与被观察者
闹钟
ContextLoaderListener
访问者模式(Visitor)
横看成岭侧成峰,远近高低各不同
解耦数据结构和数据操作
KPI考核
FileVisitor、BeanDefinitionVisitor

2.4 设计模式之间的关联和对比

单例模式和工厂模式
实际业务代码中,通常会把工厂类设计为单例。
策略模式和工厂模式
  • 工厂模式包含工厂方法模式和抽象工厂模式是创建型模式,策略模式属于行为型模式。
  • 工厂模式主要目的是封装好创建逻辑,策略模式接收工厂创建好的对象,从而实现不同的行为。
策略模式和委派模式
  • 策略模式更关注算法的可替换性
  • 委派模式更关注分发和调度的过程
代理模式与委派模式
  • 代理模式强调访问控制,代理对客户端隐藏目标对象的细节。
  • 委派模式强调职责转移,委派者将任务交给更专业的类处理。
门面模式和委派模式
  • 门面模式强调简化入口,门面是子系统的统一入口,封装实现的复杂性。
  • 委派模式强调任务分配,将具体任务分配给特定实现类。
模板方法模式和工厂方法模式
工厂方法是模板方法的一种特殊实现。
notion image
  • 对于工厂方法模式的 create() 方法而言,相当于只有一个步骤的模板方法模式。这一个步骤交给子类去实现。
  • 模板方法则将 needHomework() 方法和 checkHomework() 方法交给子类实现, needHomework() 方法和 checkHomework() 方法又属于父类的某一个步骤且不可变更。
模板方法模式和策略模式
1、 模板方法和策略模式都有封装算法。
2、 策略模式是使不同算法可以相互替换,且不影响客户端应用层的使用。
3、 模板方法是针对定义一个算法的流程,将一些有细微差异的部分交给子类实现。
4、模板方法模式不能改变算法流程,策略模式可以改变算法流程且可替换。策略模式通常用来代替 if…else…等条件分支语句。
notion image
1、WechatPay、JDPay、AliPay是交给用户选择且相互替代解决方案。而 JdbcTemplate下面的子类是不能相互代替的。
2、策略模式中的queryBalanceOTJ法虽然在pay。方法中也有调用,但是这个逻辑只是出于程序健壮 性考虑。用户完全可以自主调用que「yBalance()方法。而模板方法模式中的mapRow()方法一定要在 获 得 ResultSet之后方可调用,否则没有意义。
装饰者模式和静态代理模式
1、 装饰者模式关注点在于给对象动态添加方法,而代理更加注重控制对对象的访问。
2、 代理模式通常会在代理类中创建被代理对象的实例,而装饰者模式通常把被装饰者作为构造参数。
notion image
装饰者和代理者虽然都持有对方引用,但逻辑处理重心是不一样的。
装饰者模式和适配器模式
装饰者模式和适配器模式都是属于包装器模式(Wrapper Pattern)的实现形式。装饰者模式可以实现被装饰者与相同的接口或者继承被装饰者作为它的子类,而适配器和被适配者可以实现不同的接口。
notion image
  • 装饰者侧重于对象增强,被增强的对象需要满足 OOP 的 is-a 关系,不管如何包装都有共同的父类。
  • 适配器侧重于解决兼容问题,不一定要统一父类。上图中 LoginAdapterRegistAdapter 就是兼容不同功能的两个类,但 RegistForQQAdapter需要注册后自动登录,因此既继承了 RegistAdpter 又继承了 LoginAdapter
适配器模式和静态代理模式
适配器可以结合静态代理来实现,保存被适配对象的引用,但不是唯一的实现方式。
适配器模式和策略模式
在适配业务复杂的情况下,利用策略模式优化动态适配逻辑。

2.5 设计模式使用总结

各种设计模式使用频次:
  • 创建型模式:
    • 高频: 工厂方法模式、抽象工厂模式、单例模式、建造者模式
    • 低频 : 原型模式
  • 结构型模式:
    • 高频: 代理模式、门面模式、装饰器模式、适配器模式、组合模式
    • 低频 : 桥接模式、享元模式
  • 行为型模式:
    • 高频: 模板方法模式、策略模式、责任链模式、观察者模式
    • 低频: 备忘录模式、 迭代器模式、中介者模式、命令模式、解释器模式、访问者模式、状态模式
设计模式使用建议:
  1. 理解意图: 不要死记硬背,理解每个模式要解决什么问题。
  1. 识别场景: 多看经典应用场景,思考在你的项目中哪里可以用到。
  1. 权衡利弊: 没有完美的模式,每个模式在带来好处的同时也可能引入复杂性。
  1. 不要过度设计: 简单代码能解决的问题,就不要生搬硬套设计模式。
Spring 中的设计模式总结:Spring 就是一个把设计模式用得淋漓尽致的经典框架,其实从类的命名就能看出来:
设计模式名称
举例
工厂模式
BeanFactory
装饰器模式
BeanWrapper
代理模式
AopProxy
委派模式
DispatcherServlet
策略模式
HandlerMapping
适配器模式
HandlerAdapter
模板模式
JdbcTemplate
观察者模式
ContextLoaderListener
雪花算法介绍和实现领域驱动设计在互联网业务开发中的实践
Loading...