# 创建型模式介绍

  • 单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。
  • 原型(Prototype)模式:将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例。
  • 工厂方法(FactoryMethod)模式:定义一个用于创建产品的接口,由子类决定生产什么产品。
  • 抽象工厂(AbstractFactory)模式:提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品。
  • 建造者(Builder)模式:将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。

以上 5 种创建型模式,除了工厂方法模式属于类创建型模式,其他的全部属于对象创建型模式。

# 单例模式(Singleton)

单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。它的运作方式是这样的: 如果你创建了一个对象, 同时过一会儿后你决定再创建一个新对象, 此时你会获得之前已创建的对象, 而不是一个新对象。
单例模式有 3 个特点:

  1. 单例类只有一个实例对象;
  2. 该单例对象必须由单例类自行创建;
  3. 单例类对外提供一个访问该单例的全局访问点。

# 单例模式的结构与实现

单例模式是设计模式中最简单的模式之一。通常,普通类的构造函数是公有的,外部类可以通过 “new 构造函数 ()” 来生成多个实例。但是,如果将类的构造函数设为私有的,外部类就无法调用该构造函数,也就无法生成多个实例。这时该类自身必须定义一个静态私有实例,并向外提供一个静态的公有函数用于创建或获取该静态私有实例。

# 单例模式的实现

# 饿汉式 (静态变量)

该模式的特点是类一旦加载就创建一个单例,保证在调用 getInstance 方法之前单例已经存在了。

public class HungrySingleton {
    private static final HungrySingleton instance = new HungrySingleton();
    private HungrySingleton() {
    }
    public static HungrySingleton getInstance() {
        return instance;
    }
}

饿汉式单例在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以是线程安全的,可以直接用于多线程而不会出现问题。不过,这样的实现方式不支持延迟加载

# 饿汉式 (静态代码块)

class Singleton {
  // 1. 构造器私有化
  private Singleton() {}
  // 2. 本类内部创建对象实例
  private static Singleton instance;
  static { // 在静态代码块中,创建单例对象
    instance = new Singleton();
  }
  // 3. 提供一个公有的静态方法,返回实例对象
  public static Singleton getInstance() {
    return instance;
  }
}

# 懒汉式 (线程安全,同步方法)

该模式的特点是类加载时没有生成单例,只有当第一次调用 getlnstance 方法时才去创建这个单例。代码如下:

class LazySingleton {
    //volatile 详解  https://ifeve.com/java-volatile% E5%85% B3% E9%94% AE% E5% AD%97/
    private static volatile LazySingleton instance = null;
    private LazySingleton() {
    }
    public static synchronized LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

注意:如果编写的是多线程程序,则不要删除上例代码中的关键字 volatile 和 synchronized,否则将存在线程非安全的问题。如果不删除这两个关键字就能保证线程安全,但是每次访问时都要同步,会影响性能,且消耗更多的资源,这是懒汉式单例的缺点。

# 静态内部类

class Singleton {
    private Singleton() {
    }
    private static class SingletonHolder {
        private static final Singleton instance = new Singleton();
    }
    public static Singleton getInstance(){
        return SingletonHolder.instance;
    }
}

SingletonHolder 是一个静态内部类,当外部类 IdGenerator 被加载的时候,并不会创建 SingletonHolder 实例对象。只有当调用 getInstance () 方法时,SingletonHolder 才会被加载,这个时候才会创建 instance。instance 的唯一性、创建过程的线程安全性,都由 JVM 来保证。所以,这种实现方法既保证了线程安全,又能做到延迟加载。

# 枚举

public enum IdGenerator {
  INSTANCE;
  private AtomicLong id = new AtomicLong(0);
 
  public long getId() { 
    return id.incrementAndGet();
  }
}

# 使用场景

  • 对于一些控制硬件级别的操作,或者从系统上来讲应当是单一控制逻辑的操作,如果有多个实例,则系统会完全乱套。
  • 当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如 Web 中的配置对象、数据库的连接池等。

什么时候应该用 Singleton 呢?实际上,很多程序,尤其是 Web 程序,大部分服务类都应该被视作 Singleton,如果全部按 Singleton 的写法写,会非常麻烦,所以,通常是通过约定让框架(例如 Spring)来实例化这些类,保证只有一个实例,调用方自觉通过框架获取实例而不是 new 操作符:

@Component // 表示一个单例组件
public class MyService {
    ...
}

因此,除非确有必要,否则 Singleton 模式一般以 “约定” 为主,不会刻意实现它。