按照模式的应用目标分类,设计模式可以分为创建型模式、结构型模式和行为型模式:
- 创建型模式,是对对象创建过程的各种问题和解决方案的总结,包括各种工厂模式(Factory、Abstract Factory)、单例模式(Singleton)、构建器模式(Builder)、原型模式(ProtoType)
- 结构型模式,是针对软件设计结构的总结,关注于类、对象继承、组合方式的实践经验,常见的结构型模式,包括桥接模式(Bridge)、适配器模式(Adapter)、装饰者模式(Decorator)、代理模式(Proxy)、组合模式(Composite)、外观模式(Facade)、享元模式(Flyweight)等
- 行为型模式,是从类或对象之间交互、职责划分等角度总结的模式,比较常见的行为型模式有策略模式(Strategy)、解释器模式(Interpreter)、命令模式(Command)、观察者模式(Observer)、迭代器模式(Iterator)、模板方法模式(Template Method)、访问者模式(Visitor)
单例模式(Singleton)
目的:保证在整个应用中某一个类有且只有一个实例,而且自行实例化并向整个系统提供这个实例
工具类的设计
工具类在开发中其实只需要存在一个对象即可
- 如果工具方法没有使用
static
修饰,说明工具方法得使用工具类的对象来调用,此时应把工具类设计为单例的 - 如果工具方法全部使用
static
修饰,说明工具方法只需要使用工具类名调用即可,此时必须把工具类的构造器私有化
饿汉式(Eager Singleton)
- 在类装载的时候就创建对象实例
- 使用场景:java.lang.Runtime
public class Singleton {
// 本类中创建 private static final 修饰的本类对象实例
private static final Singleton instance = new Singleton();
// 私有化构造器
private Singleton() {}
// 提供一个 public static 修饰的方法返回此对象实例
public static Singleton getInstance() {
return instance;
}
}
public class Singleton {
// 私有化构造器
private Singleton() {}
// 静态内部类不会在单例类加载时就加载,而是在调用 getInstance() 方法时才进行加载
private static class SingletonHolder {
private static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
懒汉式 / 懒加载(Lazy Singleton)
- 每次获取实例都会进行判断,看是否需要创建实例
- “双重检查加锁”机制:不是每次进入 getInstance() 方法都需要同步,而是先不同步,进入方法后,先检查实例是否存在,如果不存在才进行下面的同步块,这是第一重检查,进入同步块过后,再次检查实例是否存在,如果不存在,就在同步的情况下创建一个实例,这是第二重检查。这样一来,就只需要同步一次了,从而减少了多次在同步情况下进行判断所浪费的时间。
public class Singleton {
// 本类中定义 private static volatile 修饰的、引用为空的类变量
private static volatile Singleton instance = null;
// 私有化构造器
private Singleton() {}
// 提供一个 public static 修饰的方法返回对象实例
public static Singleton getInstance() {
// 先检查实例是否存在,如果不存在才进入下面的同步块
if (instance == null) {
synchronized (Singleton.class) {
// 双重检测锁:再次检查实例是否存在,如果不存在才真正的创建实例
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
使用枚举类做单例模式
- 定义一个包含单个元素的枚举类型
public enum Singleton {
INSTANCE;
}
// 调用
Singleton.INSTANCE.sort(null);
模板方法模式(Template Method)
- 在采用某个算法的框架,需要对它的某些部分进行改进
在抽象父类的一个方法中定义该方法的总体算法的骨架(模板方法),而把其中某些具体的步骤(不能实现的部分)抽象成抽象方法,延迟到子类中去实现
抽象父类至少提供的方法:
- 模板方法:一种通用的处理方式,即模板(总体算法的骨架)
- 抽象方法:一种具体的业务功能实现,由子类完成
使用场景:JDBCTemplate、JUC中的 AQS
public abstract class OperateTimeTemplate{
// 模板方法:总体算法的骨架,子类不能修改
public final long getTotalTime() {
long begin = System.currentTimeMillis();
this.doWork(); // 具体的步骤(延迟到子类中去实现)
long end = System.currentTimeMillis();
return end - begin;
}
// 抽象方法
protected abstract void doWork();
}
public class StringOperate extends OperateTimeTemplate {
public void doWork() {
String str = "";
for (int i = 0; i < 10000; i++) {
str += i;
}
}
}
// 调用
new StringOperate().getTotalTime()
装饰器模式(Decorator Pattern)
- 在不必改变源代码基础上,动态地扩展一个对象的功能
- 通过创建一个包装对象,包裹真实的对象
- 使用场景:IO 流中的包装流、Servlet 中的敏感字过滤
- 特点:
- 装饰对象和真实对象有相同的接口
- 装饰对象包含一个真实对象的引用(多用组合,少用继承)
- 装饰对象接受所有来自客户端的请求,并把这些请求转发给真实的对象
- 装饰对象可以在转发这些请求以前或以后增加一些附加功能
适配器模式(Adapter Pattern)
- 将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作
代理模式(Proxy Pattern)
- 客户端不直接调用实际的对象,而是通过调用代理,来间接的调用实际的对象
策略模式(Strategy Pattern)
- 策略模式代表了解决一类算法的通用解决方案,需要在运行时选择使用哪种方案
- 定义一系列的算法,并将每一个算法封装起来,而且使它们可以相互替换,让算法独立于使用它的客户而独立变化
- 策略模式包含三部分内容
- 一个代表某个算法的接口(它是策略模式的接口)
- 一个或多个该接口的具体实现,它们代表了算法的多种实现
- 一个或多个使用策略对象的客户
观察者模式
- 某些事件发生时(比如状态转变),一个对象(主题)需要自动地通知其他多个对象(观察者)
责任链模式
- 一种创建处理对象序列(比如操作序列)的通用方案:一个处理对象可能需要在完成一些工作之后,将结果传递给另一个对象,这个对象接着做一些工作,再转交给下一个处理对象,以此类推
工厂模式
- 无需向客户暴露实例化的逻辑就能完成对象的创建
- Spring 中:BeanFactory 和 ApplicationContext
迭代器模式
- 提供一个一致的方法来顺序访问集合中的对象,这个方法与底层的集合的具体实现无关
- JDK 中:java.util.Iterator、java.util.Enumeration
享元模式
- 使用缓存来加速大量小对象的访问时间
- JDK 中:java.lang.Integer#valueOf(int)、java.lang.Boolean#valueOf(boolean)、java.lang.Byte#valueOf(byte)、java.lang.Character#valueOf(char)