【设计模式】设计模式

按照模式的应用目标分类,设计模式可以分为创建型模式、结构型模式和行为型模式。

  • 创建型模式:是对对象创建过程的各种问题和解决方案的总结,包括各种工厂模式(Factory、Abstract Factory)、单例模式(Singleton)、构建器模式(Builder)、原型模式(ProtoType)
  • 结构型模式:是针对软件设计结构的总结,关注于类、对象继承、组合方式的实践经验。常见的结构型模式,包括桥接模式(Bridge)、适配器模式(Adapter)、装饰者模式(Decorator)、代理模式(Proxy)、组合模式(Composite)、外观模式(Facade)、享元模式(Flyweight)等
  • 行为型模式:是从类或对象之间交互、职责划分等角度总结的模式。比较常见的行为型模式有策略模式(Strategy)、解释器模式(Interpreter)、命令模式(Command)、观察者模式(Observer)、迭代器模式(Iterator)、模板方法模式(Template Method)、访问者模式(Visitor)

单例模式

线程安全的懒汉式单例模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 1. final 修饰:禁止被继承,防止子类继承后破坏单例特性
public final class Singleton implements Serializable {
// 2. 私有构造方法:防止被外部直接构造,但仍然无法防止以反射的方式创建新实例
private Singleton() {}
// 3. 懒汉模式初始设置为 null。如果为饿汉模式,则需要在这里 new Singleton,其会在类被加载时被创建,也是线程安全的
// 4. volatile 修饰:保证 instance 的有序性
private static volatile Singleton instance = null;
// 5. 提供静态方法获取实例对象而不是直接将其设置为 public:保证更好的封装性,并且懒汉模式必须在调用方法时创建实例,如果用 public 修饰 instance 直接获取,就只能写成饿汉模式
public static Singleton getInstance() {
// 6. 双端检锁机制:DCL(Double Check Lock ),提供并发性
// 第一个 if 判断是为了防止创建好实例后每次获取实例都需要加锁导致并发性降低
if (instance == null){
// 对类加锁
synchronized(Singleton.class) {
// 第二个 if 判断是为了防止第一次创建对象前被并发的创建多个实例对象,在锁内再判断一次就能使得后面抢到锁的线程再去重复创建
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
// 7. 防止反序列化破坏单例:当被反序列化创建对象时,调用该方法直接返回实例,而不需要再去创建一个新的实例,保证了线程安全性
public Object readResovle() {
return instance;
}
}

其中需要注意的几个点:

  1. final 修饰 Singleton 类:禁止被继承,防止子类继承后破坏单例特性
  2. 私有构造方法:防止被外部直接构造,但仍然无法防止以反射的方式创建新实例
  3. 懒汉模式初始设置为 null。如果为饿汉模式,则需要在这里 new Singleton(),其会在类被加载时被创建,也是线程安全的
  4. volatile 修饰:保证 instance有序性
  5. 提供静态方法获取实例对象而不是直接将其设置为 public:保证更好的封装性,并且懒汉模式必须在调用方法时创建实例,如果用 public 修饰 instance 直接获取,就只能写成饿汉模式
  6. 双端检锁机制:DCL(Double Check Lock ),提供并发性
    1. 第一个 if 判断是为了防止创建好实例后每次获取实例都需要加锁导致并发性降低
    2. 第二个 if 判断是为了防止第一次创建对象前被并发的创建多个实例对象,在锁内再判断一次就能使得后面抢到锁的线程再去重复创建
  7. 防止反序列化破坏单例:当被反序列化创建对象时,调用该方法直接返回实例,而不需要再去创建一个新的实例,保证了线程安全性

享元模式

Flyweight pattern

当需要重用数量有限的同一类对象时使用。本质上就是多个对象共享同一份数据,节省系统资源。

Java 中享元模式的体现:

  • 包装类的 valueOf() 方法缓存机制
  • String 字符串常量池
  • 线程池

在JDK中 Boolean,Byte,Short,Integer,Long,Character 等包装类提供了 valueOf() 方法,例如 Long 的
valueOf() 会缓存 -128~127 之间的 Long 对象,在这个范围之间会重用对象,大于这个范围,才会新建 Long 对
象:

1
2
3
4
5
6
7
public static Long valueOf(long l) {
final int offset = 128;
if (l >= -128 && l <= 127) { // will cache
return LongCache.cache[(int)l + offset];
}
return new Long(l);
}

注意:

  • Byte, Short, Long 缓存的范围都是 -128~127
  • Character 缓存的范围是 0~127
  • Integer 的默认范围是 -128~127
    • 最小值不能变
    • 但最大值可以通过调整虚拟机参数 -Djava.lang.Integer.IntegerCache.high 来改变
  • Boolean 缓存了 true 和 false

Spring 中使用到的设计模式

单例模式 Singleton

Ensure a class only has one instance, and provide a global point of access to it

注意区分 Singleton Pattern 与 Spring 中的 Singleton Bean

根据单例模式的目的 ,显然 Spring 中的 singleton bean 并非实现了单例模式,singleton bean 只能保证每个容器内,相同 id 的 bean 单实例只有一份。id 不相同的 bean 可以重复存在

当然 Spring 中也用到了单例模式,例如

  • org.springframework.transaction.TransactionDefinition#withDefaults
  • org.springframework.aop.TruePointcut#INSTANCE
  • org.springframework.aop.interceptor.ExposeInvocationInterceptor#ADVISOR
  • org.springframework.core.annotation.AnnotationAwareOrderComparator#INSTANCE
  • org.springframework.core.OrderComparator#INSTANCE

建造者模式 Builder

Separate the construction of a complex object from its representation so that the same construction process can create different representations

它的主要亮点有三处:

  1. 较为灵活的构建产品对象
  2. 在不执行最后 build 方法前,产品对象都不可用
  3. 构建过程采用链式调用,看起来比较爽

Spring 中体现 Builder 模式的地方:

  • org.springframework.beans.factory.support.BeanDefinitionBuilder
  • org.springframework.web.util.UriComponentsBuilder
  • org.springframework.http.ResponseEntity.HeadersBuilder
  • org.springframework.http.ResponseEntity.BodyBuilder

工厂方法模式 Factory Method

Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses

工厂模式的目的:让接口和实现相分离,降低耦合。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

根据上面的定义,Spring 中的 ApplicationContextBeanFactory 中的 getBean() 都可以视为工厂方法,它隐藏了 bean (产品)的创建过程和具体实现。


https://www.cnblogs.com/yssjun/p/11102162.html

  • 简单工厂:在方法内通过判断传入的类型是什么创建出对应的对象,里面会有大量的 if/else 逻辑,不利于扩展
  • 工厂方法:提供一个 AbstractFactory,实现多个工厂子类,各自实现自己的逻辑,生产出对应的产品对象
  • 抽象工厂:同样提供一个 AbstractFactory,实现多个工厂子类,其中每个子类里提供多个方法,用于支持生产不同类型的产品对象

Spring 中其它工厂:

  • org.springframework.beans.factory.FactoryBean
  • @Bean 标注的静态方法及实例方法
  • ObjectFactory 及 ObjectProvider

前两种工厂主要封装第三方的 bean 的创建过程,后两种工厂可以推迟 bean 创建,解决循环依赖及单例注入多例等问题

适配器模式 Adapter

Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces

典型的实现有两处:

  • org.springframework.web.servlet.HandlerAdapter – 因为控制器实现有各种各样,比如有
    • 大家熟悉的 @RequestMapping 标注的控制器实现
    • 传统的基于 Controller 接口(不是 @Controller注解啊)的实现
    • 较新的基于 RouterFunction 接口的实现
    • 它们的处理方法都不一样,为了统一调用,必须适配为 HandlerAdapter 接口
  • org.springframework.beans.factory.support.DisposableBeanAdapter – 因为销毁方法多种多样,因此都要适配为 DisposableBean 来统一调用销毁方法

有的 HandlerAdapter 适配器是用来适配调用 @ReqeuestMapping,有的是适配调用基于 Controller 接口的。DispatcherServlet 通过适配器来调用 Controller 里的具体方法。

组合模式 Composite

Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly

组合模式的典型实现为各种解析器:参数解析器方法返回值解析器异常解析器视图解析器

  • org.springframework.web.method.support.HandlerMethodArgumentResolverComposite(多种不同的参数解析器组合起来,组合器内的集合就是各种参数解析器,使用组合器的统一调用接口来实现多个分散的解析器的有序调用
  • org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite
  • org.springframework.web.servlet.handler.HandlerExceptionResolverComposite
  • org.springframework.web.servlet.view.ViewResolverComposite

composite 对象的作用是将分散的调用集中起来,统一调用入口。它的特征是,与具体干活的类实现同一个接口,当调用 composite 对象的接口方法时,其实是委托具体干活的实现来完成。

装饰器模式 Decorator

Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality

装饰器模式:对一个对象动态的增加一些职责和功能。解决子类扩展方式提供功能增强的问题:子类扩展需要把所有父类的方法都继承。使用装饰器模式就可以增加一些额外的功能。

典型实现:

  • org.springframework.web.util.ContentCachingRequestWrapper
  • Spring Session 中的 SessionRepositoryRequestWrapper,将 将普通的 HttpRequest 进行了包装,这样 Controller 层在调用HttpRequest.getSession() 时,真正在执行的就是包装后的 SessionRepositoryRequestWrappergetSession() 方法了

代理模式 Proxy

Provide a surrogate or placeholder for another object to control access to it

装饰器模式注重的是功能增强,避免子类继承方式进行功能扩展,而代理模式更注重控制目标的访问。侧重控制和访问,增强其实是由切面方法实现的,代理主要是为了控制访问。

典型实现:

  • org.springframework.aop.framework.JdkDynamicAopProxy:jdk 代理
  • org.springframework.aop.framework.ObjenesisCglibAopProxy:cglib 代理

AOP 中就是用了代理模式,使用 ProyFactory 为对象创建动态代理对象。

责任链模式 Chain of Responsibility

Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it

典型实现:

  • org.springframework.web.servlet.HandlerInterceptor

各种拦截器都是责任链模式,每一个拦截器只负责调用下一个拦截器的方法,最后一个拦截器再执行目标方法。

观察者模式 Observer

Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically

典型实现:

  • org.springframework.context.ApplicationListener:接收事件
  • org.springframework.context.event.ApplicationEventMulticaster:发送事件
  • org.springframework.context.ApplicationEvent:事件

策略模式 Strategy

Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it

典型实现:

  • org.springframework.beans.factory.support.InstantiationStrategy
  • org.springframework.core.annotation.MergedAnnotations.SearchStrategy:查找注解时,使用多个策略,判断是去当前类中查找呢还是父类查找呢
  • org.springframework.boot.autoconfigure.condition.SearchStrategy:条件判断,当前bean是在哪个容器里呢

根据开发人员的选择,执行相应的策略。

模板方法模式 Template Method

Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure

典型实现:

  • 大部分以 Template 命名的类,如 JdbcTemplateTransactionTemplate
  • 很多以 Abstract 命名的类,如 AbstractApplicationContext

父类没有实现方法内容,只是提供一个基础架构。子类继承后自己实现。JUC 里也有很多模板方法模式的实现。