经典设计模式共分为 3 种类型,分别是创建型、结构型和行为型。今天,我们先看下创建型设计模式的原理、实现、设计意图和应用场景。
创建型设计模式
创建型设计模式包括:单例模式、工厂模式、建造者模式、原型模式。它主要解决对象的创建问题,封装复杂的创建过程,解耦对象的创建代码和使用代码。
1. 单例模式
单例模式用来创建全局唯一的对象。一个类只允许创建一个对象(或者叫实例),那这个类就是一个单例类,这种设计模式就叫作单例模式。单例有几种经典的实现方式,它们分别 是:饿汉式、懒汉式、双重检测、静态内部类、枚举。
尽管单例是一个很常用的设计模式,在实际的开发中,我们也确实经常用到它,但是,有些 人认为单例是一种反模式(anti-pattern),并不推荐使用,主要的理由有以下几点:
- 单例对 OOP 特性的支持不友好
- 单例会隐藏类之间的依赖关系
- 单例对代码的扩展性不友好
- 单例对代码的可测试性不友好
- 单例不支持有参数的构造函数
那有什么替代单例的解决方案呢?如果要完全解决这些问题,我们可能要从根上寻找其他方 式来实现全局唯一类。比如,通过工厂模式、IOC 容器来保证全局唯一性。有人把单例当作反模式,主张杜绝在项目中使用。我个人觉得这有点极端。模式本身没有对 错,关键看你怎么用。如果单例类并没有后续扩展的需求,并且不依赖外部系统,那设计成单例类就没有太大问题。对于一些全局类,我们在其他地方 new 的话,还要在类之间传来传去,不如直接做成单例类,使用起来简洁方便。
除此之外,单例有进程唯一单例、线程唯一单例、集群唯一单例、多例等扩展,这一部分在实际的开发中并不会被用到,但是可以扩展你的思路、锻炼你的逻辑思维。
示例
//双重检测
//没加volatile 是因为高版本的 Java 已经在 JDK 内部实现中解决了这个问题
//(解决的方法很简单,只要把对象 new 操作和初始化操作 设计为原子操作,就自然能禁止重排序)
public class IdGenerator {
private static IdGenerator instance;
private AtomicLong id = new AtomicLong(0);
private IdGenerator() {
}
public static IdGenerator getInstance() {
if (instance == null) {
synchronized (IdGenerator.class) {
// 此处为类级别的锁
if (instance == null) {
instance = new IdGenerator();
}
}
}
return instance;
}
public long getId() {
return id.incrementAndGet();
}
}
//静态内部类
public class IdGenerator {
private AtomicLong id = new AtomicLong(0);
private IdGenerator() {
}
public long getId() {
return id.incrementAndGet();
}
private static class SingletonHolder {
private static final IdGenerator instance = new IdGenerator()