创建型模式
单例模式
单例模式保证系统实例的唯一性。首先需要将构造方法私有化,避免外部程序创建该类实例。然后提供一个全局静态方法获取该类实例,用户也只能通过该方法获取实例。
- 构造函数私有化
- 提供一个方法获取实例(因为外部不能创建该类对象,所以提供的方法只能是静态方法,以及提供的实例变量也是静态变量)
单例模式常见实现:
- 懒汉模式(要用的时候才创建实例)
- 饿汉模式(类加载的时候就创建实例)
- 静态内部类(类加载的时候就创建实例)
- 双重锁校验
懒汉模式
public class LazySingleton {
// 这里必须是私有静态变量
private static LazySingleton instance;
// 1. 构造函数私有化
private LazySingleton() {}
// 2. 提供一个方法获取实例
public static synchronized LazySingleton getInstance() {
if(instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
饿汉模式
public class HungrySingleton {
// 类加载时候就创建实例
private static HungrySingleton instance = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return instance;
}
}
静态内部类
public class Singleton {
static class SingletonHolder() {
// 静态内部类来创建外部类实例,在类加载过程的初始化阶段,就会创建实例
private static final Singleton instance = new Singleton();
}
private Singleton() {}
public static final Singleton getInstance() {
return SingletonHodler.instance;
}
}
双重锁校验
public class LazySingleton {
// 这里必须是私有静态变量
private volatile static LazySingleton instance;
// 1. 构造函数私有化
private LazySingleton() {}
// 2. 提供一个方法获取实例
public static LazySingleton getInstance() {
if(instance == null) {
synchronized(LazySingleton.class) {
if(instance == null) {
instance = new LazySingleton();
}
}
}
return instance;
}
}
反射问题
Class对象的Constructor.newInstance()方法可以调用私有的构造函数创建对象,即可以通过反射获取对象实例。
反射可以获取类中的方法、构造函数,并修改它们的访问权限。
这里以HungrySingleton举例(LazySingleton一样)
public class TestSingleton {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
HungrySingleton instance = HungrySingleton.getInstance();
System.out.println(instance);
Constructor<HungrySingleton> constructor = HungrySingleton.class.getDeclaredConstructor();
// 这里通过反射又实例化出了一个新的对象
constructor.setAccessible(true);
HungrySingleton hungrySingleton = constructor.newInstance();
System.out.println(hungrySingleton);
}
}
可以给构造函数做条件判断,判断静态变量是否已初始化,如果初始化过就直接抛异常。但是这种方式也会有问题,如果先通过反射创建对象,那对于懒汉模式的静态变量还是null,后面再通过getInstance方法又会创建一个新的实例处理。
枚举实现单例
原型模式
调用实例的clone方法或其他方式来创建对象。
Java中的复制分为浅复制和深复制:
- 浅复制:不复制对象的引用类型,基本数据类型重新创建并赋值,引用类型直接指向原有对象的引用。
- 深复制:基本类型和引用类型都重新创建和复制。通过重新引用类型的clone方法,实现深拷贝。
代理模式
通过代理对象实现对原对象方法的访问。
- 原本方式:直接实例化实现了目标接口的目标类对象
- 代理方法:实例化实现了目标接口的代理类对象,由代理对象内部创建并持有目标类对象,并代理目标对象的方法调用。
- 代理和对象都实现目标接口;
- 代理并持有对象,在方法内部调用对象。
外观模式
外观模式也叫门面模式,Facade类对目标类的细节进行屏蔽,对外只提供必需的接口和数据。
Tomcat与Servlet的交互利用了门面模式。
工厂模式
提供一种创建对象的模式。
通俗讲就是通过工厂方法替代new操作创建对象,将创建对象的过程封装在工厂方法里。
- 定义接口
- 定义实现类
- 定义工厂类,提供create方法,在create方法内创建实现类并返回实例
抽象工厂模式
提供了一种创建工厂的工厂模式,是一种对工厂的封装。
抽象工厂封装了创建对应工厂的方法,然后再由对应工厂来创建对象。
结构型模式
桥接模式
Bridge Pattern 抽象和实现解耦。主要解决需求多变,使用类继承方式情况下导致的类多问题。将继承关系转为关联关系,降低类与类之间的耦合。
将抽象和实现解耦,通过关联方式实现。
享元模式
Flyweight Pattern 通过对象的复用来减少对象的创建次数和数量。
一般使用场景是:需要一个对象时,享元模式会先从缓存里查找使用现有对象,如果没找到,再创建新的对象并加入缓冲中。
特别是针对大对象,创建销毁非常耗时,所以可以通过重用共享的方式来使用大对象
代码设计:
- 一般会定义一个FlyweightFactory享元工厂,内部持有一个容器保存一批用于复用的对象。
- 每次get时会先从容器中检查对象是否存在,存在则直接返回使用;不存在,则新创建对象放入容器中,再返回使用。
门面模式
Facade Pattern 对多个子系统的封装,对外提供一个统一的访问入口。降低调用者的访问难度,无需去了解各个系统之间的关系。
组合模式
Composite Pattern 又叫部分成体模式。
行为型模式
策略模式
为同一个行为定义不同策略,并为每个策略都实现了不同的方法,系统根据不同策略自动切换行为。
特点就是可以将不同的计算方法进行封装
策略模式的实现是在接口中定义不同的策略,由实现类完成策略的具体实现,并为用户提供一个策略状态存储上下文(Context)中完成策略的存储和状态的改变。
- 定义策略接口
- 实现多个策略接口实现类
- 定义策略上下文,持有所有策略实现类通过用户传入策略ID获取对应策略实现(选择策略逻辑由上下文实现),或者用户直接传入策略实现类(选择策略逻辑由用户实现)
不足之处
- 调用者需要知道所有的策略,调用的时候需要指定具体的策略
- 业务场景非常多时,会造成有非常多的策略类需要去维护
模板模式
父类实现公共部分算法,子类实现可变部分的算法。父类一般定义为抽象类,实现基础代码,提供抽象方法,将具体实现延迟到子类中。
- 定义抽象类:实现基础代码(内部调用抽象模板方法),提供抽象模板方法
- 子类继承抽象类:实现抽象模板方法,该方法由父类调用
观察者模式
被观察者状态发生变化时,会通知订阅的观察者对象,以完成状态的传播。通知常常以事件驱动方式实现。
- 事件监听
- 发布订阅
命令模式
Command Pattern 将请求封装成命令,基于事件驱动进行异步执行,实现发送者和执行者的解耦。
命令模式一般有这四个角色:
- Command 命令接口或抽象命令类
- Concrete Command 具体命令实现
- Receiver 命令执行者
- Invoker 命令发送者