设计模式(DesignPattern)
2023-09-24 20:55:17 1 举报
AI智能生成
Design Pattern
作者其他创作
大纲/内容
创建型
单例模式
概念
Singleton Design Pattern
一个类只允许创建一个对象(或者实例)
场景
处理资源访问冲突
表示全局唯一类
从业务概念上,如果有些数据在系统中只应保存一份,那就比较适合设计为单例类。比如,配置信息类。在系统中,我们只有一个配置文件,当配置文件被加载到内存之后,以对象的形式存在,也理所应当只有一份。 如果有两个对象有可能会出现重复的数据
创建模式
饿汉模式
线程安全,不支持懒加载
无锁,执行效率高
IDEA单例模式的实现方式
有锁饱汉模式
支持懒加载,线程安全
可以在多线程下工作,适用于对适用不太频繁的多线程环境下
无锁饱汉模式
支持懒加载,线程不安全,建议在单线程下使用
Holder模式(静态内部类)
线程安全,支持懒加载
当调用 getInstance() 方法时,SingletonHolder 才会被加载,这个时候才会创建 instance。insance 的唯一性、
创建过程的线程安全性,都由 JVM 来保证。所以,这种实现方法既保证了线程安全,又能做到延迟加载。
创建过程的线程安全性,都由 JVM 来保证。所以,这种实现方法既保证了线程安全,又能做到延迟加载。
双重检测模式(DCL)
线程安全,支持懒加载
高版本的 Java 已经在 JDK 内部实现中解决了这个问题(解决的方法很简单,只要把对象 new 操作和初始化操作设计为原子操作,就自然能禁止重排序, 所以说可以不加 volatile 关键字
通过枚举创建
线程安全
示例
JDK Runtime 类
采用了饿汉式的单利模式实现
工厂模式
概念
Factory Design Pattern
分类
简单工厂模式
将同一类型的对象(实现统一接口的对象),封装在统一工厂类里创建,
工厂方法模式
在简单工厂的基础上,将各个对象的创建,依次封装在各自工厂类中。
使用场景:
当创建对象是一个“大工程”的时候,选择使用工厂模式,来封装对象复杂的创建过程,将对象的创建和使用分离,让代码更加清晰
当对象的创建逻辑比较复杂,不只是简单的 new 一下就可以,而是要组合其他类对象,做各种初始化操作的时候,我们推荐使用工厂方法模式,将复杂的创建逻辑拆分到多个工厂类中,让每个工厂类都不至于过于复杂
而使用简单工厂模式,将所有的创建逻辑都放到一个工厂类中,会导致这个工厂类变得很复杂。
抽象工厂模式
提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类
建造者模式
概念
Builder Design Pattern
场景
类的构造函数必填属性很多,通过set设置,没有办法校验必填属性
如果类的属性之间有一定的依赖关系,构造函数配合set方式,无法进行依赖关系和约束条件校验
需要创建不可变对象,不能暴露set方法。
(前提是需要传递很多的属性,如果属性很少,可以不需要建造者模式)
(前提是需要传递很多的属性,如果属性很少,可以不需要建造者模式)
实现方式
把构造函数定义为private,定义public static class Builder 内部类,通过Builder 类的set方法设置属性,调用build方法创建对象。
与工厂模式的区别
工厂模式:创建不同的同一类型对象(集成同一个父类或是接口的一组子类),由给定的参数来创建哪种类型的对象;
建造者模式:创建一种类型的复杂对象,通过很多可设置参数,“定制化”的创建对象
示例
Java Calendar类 内部类Builder实现了建造者模式。
Google Guava CacheBuilder
原型模式
概念
Prototype Design Pattern
如果对象的创建成本比较大,而同一个类的不同对象之间差别不大(大部分字段都相同),在这种情况下,我们可以利用对已有对象(原型)进行复制(或者叫拷贝)的方式来创建新对象,以达到节省创建时间的目的。这种基于原型来创建对象的方式就叫作原型设计模式(Prototype Design Pattern),简称原型模式。
实现方式
浅拷贝:浅拷贝只会复制对象地址
深拷贝:深拷贝会复制对象本身
结构型
Wrapper 模式
代理模式
概念
Proxy Design Pattern
在不改变原始类(或叫被代理类)代码的情况下,通过引入代理类来给原始类附加功能。
实现方式
代理类和原始类实现同样的接口,通过委托的方式调用原始类;
通过继承扩展附加功能;
动态代理方式,运行的时候动态地创建原始类对应的代理类(相对1、2中的静态代理,不需要针对每个类都创建一个代理类);
使用场景
业务系统的非功能性需求开发
监控、统计、鉴权、限流、事务、幂等、日志(搜集接口请求信息)
代理模式在 RPC、缓存中的应用
装饰器模式
概念
Decorator Design Pattern
在不改变原有对象的基础之上,将功能附加到对象上。提供了比继承更有弹性的替代方案(扩展原有对象功能)
装饰器类和原始类继承同样的父类,这样我们可以对原始类“嵌套”多个装饰器类
装饰器类是对功能的增强,这也是装饰器模式应用场景的一个重要特点
示例
Java IO类库
Collections 中内部类UnmodifiableCollection、CheckedCollection、SynchronizedCollection就是针对Collection类的装饰器类。
Google Guava 的collect中Forwarding相关类都是缺省 Wrapper 类
Spring Cache TransactionAwareCacheDecorator增强对事务的支持
Mybatis Cache模块
在BaseCache类基础上封装了多种装饰器类,实现可插拔,可自由配置
Mybatis 的二级缓存的实现
对CachingExecutor对BaseExecutor类进行增强
适配器模式
概念
Adapter Design Pattern
用来做适配的,它将不兼容的接口转换为可兼容的接口,让原本由于接口不兼容而不能一起工作的类可以一起工作
实现方式
类适配器
使用继承关系来实现
对象适配器
使用组合关系来实现
场景
适配器模式可以看作一种“补偿模式”,用来补救设计上的缺陷。应用这种模式算是“无奈之举”
封装有缺陷的接口设计
统一多个类的接口设计
示例
Collections类 迭代器新老版本兼容
Slf4j:框架为了统一各个不同的日志框架(Log4j、JCL、Logback 等),提供了一套统一的日志接口
桥接模式(了解)
概念
Bridge Design Pattern
将抽象和实现解耦,让它们可以独立变化
"抽象": 指的并非“抽象类”或“接口”,而是跟具体的业务无关的、被抽象出来的一套“类库”
"实现": 并非指“接口的实现类”,而是跟具体业务相关的一套“类库”
应用
JDBC 驱动是桥接模式的经典应用
门面模式
概念
Facade Design Pattern
门面模式为子系统提供一组统一的接口,定义一组高层接口让子系统更易用
优势
解决易用性问题
从隐藏实现复杂性,提供更易用接口
解决性能问题
通过将多个接口调用替换为一个门面接口调用,减少网络通信成本,提高 调用者响应速度
实际应用
微服务业务聚合层下层服务通用接口设计,区分普通接口门面接口
组合模式
概念
Composite Design Pattern
将一组对象组织(Compose)成树形结构,以表示一种“部分 - 整体”的层次结构。组合让客户端(在很多设计模式书籍中,“客户端”代指代码的使用者。)可以统一单个对象和组合对象的处理逻辑
案例
文件系统:文件(file)、文件夹(directory)
OA系统:部门(department)、员工(employee)
享元模式
概念
Flyweight Design Pattern
所谓“享元”,顾名思义就是被共享的单元。享元模式的意图是复用对象,节省内存,前提是享元对象是不可变对象
享元模式 vs 单例、缓存、对象池
在单例模式中,一个类只能创建一个对象,而在享元模式中,一个类可以创建多个对象,每个对象被多处代码引用共享。实际上,享元模式有点类似于之前讲到的单例的变体:多例。
在享元模式的实现中,我们通过工厂类来“缓存”已经创建好的对象。这里的“缓存”实际上是“存储”的意思,跟我们平时所说的“数据库缓存”“CPU 缓存”“MemCache 缓存”是两回事。我们平时所讲的缓存,主要是为了提高访问效率,而非复用。
池化技术中的“复用”可以理解为“重复使用”,主要目的是节省时间(比如从数据库池中取一个连接,不需要重新创建)。在任意时刻,每一个对象、连接、线程,并不会被多处使用,而是被一个使用者独占,当使用完成之后,放回到池中,再由其他使用者重复利用。享元模式中的“复用”可以理解为“共享使用”,在整个生命周期中,都是被所有使用者共享的,主要目的是节省空间。
享元模式在Integer、String中的应用
Integer 用到了享元模式来复用对象,如果要创建的 Integer 对象的值在 -128 到 127 之间,会从 IntegerCache 类中直接返回,否则才调用 new 方法创建
String 类利用享元模式来复用相同的字符串常量(也就是代码中的“小争哥”)。JVM 会专门开辟一块存储区来存储字符串常量,这块存储区叫作“字符串常量池”。
实际开发的对象引用
行为型
观察者模式
概念
观察者模式(Observer Design Pattern)也被称为发布订阅模式(Publish-Subscribe Design Pattern)
在对象之间定义一个一对多的依赖,当一个对象状态改变的时候,所有依赖的对象都会自动收到通知
观察者模式是一个比较抽象的模式,根据不同的应用场景和需求,有完全不同的实现方式
生产者-消费者模型 与 观察者模式区别
发布-订阅模型,算是生产者消费者的一种,存在者主动被动关系,一般是一对多的关系,订阅者之间没有竞争关系,可以以同步的方式实现,也可以以异步的方式实现。
生产-消费模型,一条消息只会被一个消费者消费,消费者之间存在竞争关系,一般以异步的方式实现
EventBus
消息总线,提供实现了观察者模式的骨架代码,Google Guava EventBus 是比较知名的一个EventBus框架,不仅支持异步非阻塞模式,还支持同步阻塞模式
示例
Google Guava EventBus
JDK 中Observable(被观察者)、Observer(观察者)
模板模式
概念
Template Pattern
模板方法模式在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。模板方法模式可以让子类在不改变算法整体结构的情况下,重新定义算法中的某些步骤。
作用
复用
模板模式把一个算法中不变的流程抽象到父类的模板方法 templateMethod() 中,将可变的部分 method1()、method2() 留给子类 ContreteClass1 和 ContreteClass2 来实现
示例
InputStream
Java AbstractList
扩展
不是指代码的扩展性,而是指框架的扩展性
示例
Java Servlet
JUnit TestCase
Google Guava Cache 的封装fetchData()方法留给子类实现
示例
Collections 类的 sort() 函数
Callback回调函数
概念
回调函数与模板模式一样,都可以起到复用、扩展的作用
相对于普通的函数调用来说,回调是一种双向调用关系。A 类事先注册某个函数 F 到 B 类,A 类在调用 B 类的 P 函数的时候,B 类反过来调用 A 类注册给它的 F 函数。这里的 F 函数就是“回调函数”。A 调用 B,B 反过来又调用 A,这种调用机制就叫作“回调”。
示例
Spring中的JdbcTemplate、RedisTemplate 、RabbitTemplate(执行完消息发送)
策略模式
概念
Strategy Design Pattern
定义一族算法类,将每个算法分别封装起来,让它们可以互相替换。策略模式可以使算法的变化独立于使用它们的客户端(这里的客户端代指使用算法的代码)
三部曲
定义:一个策略接口和一组实现这个接口的策略类
创建:策略模式会包含一组策略,在使用它们的时候,一般会通过类型(type)来判断创建哪个策略来使用。为了封装创建逻辑,我们需要对客户端代码屏蔽创建细节。我们可以把根据 type 创建策略的逻辑抽离出来,放到工厂类中
使用
策略模式包含一组可选策略,客户端代码一般如何确定使用哪个策略呢?最常见的是运行时动态确定使用哪种策略,这也是策略模式最典型的应用场景。
示例
limit分页查询优化
可以利用策略模式,优化分页查询:当偏移量(offset)< 100时,执行正常sql,当offset > 100时,执行优化sql
职责链模式/责任链模式
概念
Chain Of Responsibility Design Pattern
将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。将这些接收对象串成一条链,并沿着这条链传递这个请求,直到链上的某个接收对象能够处理它为止。
职责链模式的变体
请求会被所有的处理器都处理一遍,不存在中途终止的情况
实现方式
链表(head + tail) + 模板方法模式
数组:将处理器放进一个list里,for循环调用所有的处理器
场景
过滤敏感信息
Servlet filter
Servlet Filter 主要拦截 Servlet 请求
Spring Interceptor
Spring Interceptor 主要拦截 Spring 管理的 Bean 方法(比如 Controller 类的方法等)
Mybatis Plugin
MyBatis Plugin 主要拦截的是 MyBatis 在执行 SQL 的过程中涉及的一些方法
Spring Security核心原理
通过数组实现的
picture
状态模式
概念
状态模式是状态机的一种实现方式
有限状态机
Finite State Machine,缩写为 FSM,简称为状态机,状态机有 3 个
组成部分
状态(State)
事件(Event)
事件触发状态的转移及动作的执行。不过,动作不是必须的
动作(Action)
状态机的实现方式
if/switch逻辑
查表法
状态模式
有限状态机
迭代器模式
迭代器模式(Iterator Design Pattern),也叫作游标模式(Cursor Design Pattern)。
设计思路
迭代器中需要定义 hasNext()、currentItem()、next() 三个最基本的方法。
待遍历的容器对象通过依赖注入传递到迭代器类中。
容器通过 iterator() 方法来创建迭代器。
优势
访问者模式(了解)
概念
Visitor Design Pattern: 允许一个或者多个操作应用到一组对象上,解耦操作和对象本身
访问者模式其实是Single Dispatch 来模拟 Double Dispatch 的实现过程
picture
备忘录模式
概念
备忘录模式也叫快照模式
Memento Design Pattern:在不违背封装原则的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便之后恢复对象为先前的状态
场景
备忘录模式的应用场景也比较明确和有限,主要是用来防丢失、撤销、恢复等
高频全量备份,低频增量备份
MySQL数据库低频全量备份,结合binlog增量备份,来恢复数据
命令模式
概念
**Command Design Pattern**: 命令模式将请求(命令)封装为一个对象,这样可以使用不同的请求参数化其他对象(将不同请求依赖注入到其他对象),并且能够支持请求(命令)的排队执行、记录日志、撤销等(附加控制)功能
场景
命令模式的主要作用和应用场景,是用来控制命令的执行,比如,异步、延迟、排队执行命令、撤销重做命令、存储命令、给命令记录日志等等,这才是命令模式能发挥独一无二作用的地方。
解释器模式
概念
解释器模式为某个语言定义它的语法(或者叫文法)表示,并定义一个解释器用来处理这个语法。
中介模式
概念
Mediator Design Pattern
中介模式定义了一个单独的(中介)对象,来封装一组对象之间的交互。将这组对象之间的交互委派给与中介对象交互,来避免对象之间的直接交互
场景
现实场景:塔台
模式对比
策略模式与工厂模式
待补充
装饰者模式与职责链模式
相同点
都对对象进行增强
不同点
装饰者模式,是结构型设计模式,持有被装饰的对象,并具备被装饰者的行为,对其行为进行补充增强。装饰者它可以对某一类的对象进行增强,这样就避免了冗余的继承体系,使得扩展性很强。
职责链模式,是行为型设计模式,每个被调用者都持有下一个被调用者的引用,客户端只需要发起一次调用即可。核心思想就是分而治之。当调用者面临的被调者太多时,为了降低逻辑复杂度,把相关的被调用者组织起来,形成一个链式的结构,被调用者之间进行调用传递(责任传递)。
immutable 模式
概念
不变模式
普通不变模式
对象中包含的引用对象是可以改变的(类似于浅拷贝)
深度不变模式
对象包含的引用对象也不可变(类似于深拷贝)
示例
Google Guava 针对集合类(Collection、List、Set、Map…)提供了对应的不变集合类(ImmutableCollection、ImmutableList、ImmutableSet、ImmutableMap…)
0 条评论
下一页