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