修订记录 | 时间 |
---|---|
首次发布 | 2023.07 |
一、单例模式
单例模式是一种创建型的设计模式,保证一个类只有一个实例,且提供一个可以全局访问的入口。构造方法为私有的。
优点
- 只有一个实例,节省了内存,避免了频繁创建和销毁实例。
- 因为只有一个实例,可以很方便地控制客户访问的实例。
缺点
- 没有接口,扩展不方便。
- 与单一职责原则冲突,一个类应关心内部逻辑,即产品定义,不需要关心实例化。
1. 饿汉式
在类加载时就实例化,线程安全,但可能会浪费内存,加载了不需要的类的实例。
public class HungrySingleton {
// 构造方法
private HungrySingleton() {}
// 类加载的时候就实例化
private static final instance = new HungrySingleton();
// 获取实例的入口
public static HungrySingleton getInstance() {
return instance;
}
}
2. 懒汉式
在第一次使用时初始化,可能存在线程安全问题,但节省了内存。
2.1 线程不安全
public class LazySingleton1 {
// 构造方法
private LazySingleton1() {}
// instance实例先不加载
private static instance;
// 获取实例的入口
public static LazySingleton1 getInstance() {
// 线程不安全
if (instance == null) {
instance = new LazySingleton1();
}
return instance;
}
}
2.2 线程安全但效率低
为了避免并发问题,可以给getInstance方法加synchronized锁。
public class LazySingleton2 {
// 构造方法
private LazySingleton2() {}
// instance实例先不加载
private static instance;
// 获取实例的入口
public static synchronized LazySingleton2 getInstance() {
// 并发问题只出现在创建时,但synchronized锁会影响到创建完成后
if (instance == null) {
instance = new LazySingleton2();
}
return instance;
}
}
2.3 双重校验锁Double-Checked Locking(建议写法)
为了提升小轮车,只针对创建时加synchronized锁。
public class LazySingleton3 {
// 构造方法
private LazySingleton3() {}
// instance实例先不加载
private static instance;
// 获取实例的入口
public static LazySingleton3 getInstance() {
// 仅针对创建时的并发问题进行加锁
if (instance == null) {
// 对这个类加锁
synchronized(LazySingleton3.class) {
// 防止一个线程创建时,另一个线程在等待获取synchronized锁
// 所以需要再次判断instance是否为null
if (instance == null) {
instance = new LazySingleton3();
}
return instance;
}
}
return instance;
}
}
二、工厂模式
单例模式是一种创建型的设计模式,将对象的创建与使用代码分离,提供一种统一的接口来创建不同类型的对象。
优点
- 一个调用者想创建一个对象,只要知道其名称就可以了。
- 扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
- 屏蔽产品的具体实现,调用者只关心产品的接口。
缺点
- 每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加。
JDK 中的工厂设计模式实例
- Java.util.Calendar, ResourceBundle and NumberFormat getInstance() 使用了工厂方法模式;
- valueOf() 在包装类中,如Boolean, Integer也使用了工厂方法模式;
在下面示例中,用shape和具体形状来举例。
1. 简单工厂
在创建类别较少的时候可以使用if或map来区分具体的实现类。
有三个角色:工厂类、抽象产品(接口)和具体产品(实现类)。
public class SimpleFactory1 {
public static Shape createShape(String type) {
if ("CIRCLE".equals(type)) {
return new Circle();
} else if ("TRIANGLE".equals(type)) {
return new Triangle();
}
return null;
}
}
// 使用
Circle circle = (Circle) SimpleFactory1.createShape("CIRCLE");
// 当对象全局唯一时可以使用map
public class SimpleFactory2 {
public static final Map<String, Shape> cachedShape = new HashMap<String, Shape>();
// 静态代码块会在类加载的时候将map初始化
static {
cachedShape.put("CIRCLE", new Circle());
cachedShape.put("TRIANGLE", new Triangle());
}
public static Shape getShape(String type) {
Shape shape = cachedShape.get(type);
return shape;
}
}
// 使用
Circle circle = (Circle) SimpleFactory2.getShape("CIRCLE");
2. 工厂方法
简单工厂在每次添加新产品就需要修改工厂类。在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展维护。且如果创建对象时有很复杂的逻辑,堆砌if-else会很臃肿。
工厂方法有四个角色:抽象工厂(接口)、具体工厂(实现类)、抽象产品(接口)和具体产品(实现类)。
示例中省略抽象产品和具体产品。
// 抽象工厂
public interface ShapeFactory {
Shape createShape();
}
// 具体工厂
public class CircleFactory implements ShapeFactory {
@Override
public Circle createShape() {
Circle circle = new Circle();
// 其他逻辑
// ...
return circle;
}
}
// 使用
CircleFactory circleFactory = new CircleFactory();
Circle circle = circleFactory.createShape();
3. 抽象工厂
当产品可以分为多个产品族的时候,可以使用抽象工厂使结构更清晰。抽象工厂模式主要用于创建相关对象的家族。当一个产品族中需要被设计在一起工作时,通过抽象工厂模式,能够保证客户端始终只使用同一个产品族中的对象。
抽象工厂中也有四个角色:抽象工厂、具体工厂、抽象产品(族)、具体产品(族)。
举例:服装品牌A和品牌B都有T恤和牛仔裤卖,在搭配时需要是同品牌的。
// 抽象工厂
public interface AbstractFactory {
Tshirt buyTshirt();
Jeans buyJeans();
}
// 具体工厂
public class BrandAFactory implements AbstractFactory {
@Override
public TshirtA buyTshirt() {
return new TshirtA();
}
@Override
public JeansA buyJeans() {
return new JeansA();
}
}
// 使用
BrandAFactory brandAFactory = new BrandAFactory();
TshirtA tshirtA = brandAFactory.buyTshirt();
JeansA jeansA = breandAFactory.buyJeans();
三、建造者模式
建造者模式是一种创建型的设计模式,需要和构造函数、set方法、工厂方法区分开。
方法 | 对比 |
---|---|
构造函数 | 多参数时入参很长,且增加参数需要改动构造函数 |
set方法 | 相比构造函数更灵活,但无法在构造时校验必填项,且参数可以用set方法再次修改 |
工厂模式 | 工厂模式适用于一个type字段来区分不同类对象,不适合一个对象下多个可配入参 |
建造者模式的优点:
- 构造完成后不可变,无法通过set来修改。
- 对于对象的校验可以在build时统一进行,比如必填校验、参数范围校验。
public class PostRequest {
private String api;
private String bodyStr;
// 构造方法为私有
private PostRequest(Builder builder) {
this.api = builder.api;
this.bodyStr = builder.bodyStr;
}
// 对象类内只有getter,没有setter
public String getApi() {
return api;
}
public String getBodyStr() {
return bodyStr;
}
// Builder也可以是非内部类
public static class Builder {
private String api;
private String bodyStr;
public Builder() {}
// set方法
public Builder setApi(String api) {
this.api = api;
return this;
}
public Builder setBodyStr(String bodyStr) {
this.bodyStr = bodyStr;
return this;
}
// 构造
public PostRequest build() {
// 可以进行校验
if (StringUtils.isBlank(api) {
throw new IllegalArgumentException("api missing");
}
// 返回构造的对象
return new PostRequest(builder);
}
}
}
// 使用
PostRequest postRequest = new PostRequest.Builder()
.setApi("api")
.setBodyStr("body content")
.build();