创建者模式,顾名思义,就是提供友好的创建对象的方式 ,对象都是 new 出来的,但是在一些情况下,这种方式不是很友好,首先,它不够直观,其次,在一些情况下,这样创建的对象不满足要求。 —— 比如 : 当我们需要创建单例的对象,那我们就不能每次用的时候就重新 new, 这样很明显是不合理的 。
引入工厂类的原因 : 是因为我们需要两个或者两个以上的工厂生产对象,假如我们就需要一个对象的工厂,那就没必要引入工厂类了 。
我们需要两个及以上的工厂,所以抽象出一个工厂接口,其他工厂实现类实现这个抽象接口,重写创建方法 。
涉及到产品族问题 , 调用的时候先选择工厂,再调用其方法,生成相应的产品。
Spring 框架通过反射机制实现工厂模式,从而降低了程序的耦合程度 。具体实现思路为 : 读取配置文件中相关值, 然后通过反射 Class.forName 得到该值对应的类,再通过 newInstance() 获取该类的实例返回 。
我们调用时只需要 : Factory.getXXX("我们定义在配置文件中的 Bean 名称")
私有构造方法 + 创建静态实例
public class Singleton {
// 私有构造方法
private Singleton() {};
// 创建私有静态实例
private static Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
}
缺点 : 不想用 Singleton 实例时,它也生成了,因为是 static 的, 这个类被第一次使用时就会被生成 。
私有构造方法 + volatile 修饰 + 双重判空,加锁
public class Singleton {
// 私有构造方法
private Singleton() {}
// valitale 修饰
private static volatile Singleton instance = null;
public static Singleton getInstance() {
// 双重检查是否为 null
if (instance == null) {
// 加锁
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
因为 volatile 只可以保证变量的可见性 和 防止实例化时指令重排 ,它不能保证操作的原子性 。我们要求只能有一个线程去实例化,不能一堆线程一起进去实例化 。
因为我们得保证 一个线程实例化 Singleton 后,其他线程可见,直接返回就行,所以我们需要 volatile 去保证 Singleton 在多个线程中的可见性 , 不被 JVM 缓存。
除此之外,它也通过防止对象创建时的指令重排,而使得线程安全 。
什么是指令重排 ?
创建对象时,先分配内存空间,然后实例指向该空间地址,然后才初始化 。
正常情况应该是, 分配内存空间 — 初始化 — 指向该空间地址
如果不用 volatile 修饰, 发生指令重排时, 创建对象的线程只是指向了空间地址,但是还没初始化,但是因为已经指向地址了,下一个线程在第一重 null 检查时,判断为非 null,直接返回了,但是返回的实例还没初始化 。—— 这是线程不安全的,所以需要 volatile 。
利用嵌套类可以 访问外部类 的属性和方法 。在嵌套类中实例化 Singleton 。
public class Singleton {
private Singleton() {}
// 利用嵌套类可以访问外部类的属性和方法
private static class Holder {
private static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return Holder.instance;
}
}
据说被谷歌大佬作为实现单例的最佳实践 —— 这个还简单、还安全 , 那我们学上面那些干啥 ???
public enum Singleton {
INSTANCE;
}
链式调用方法,实现对象的创建 。 —— 为什么优于 new ?
首先,一个设计模式是最佳实践是相对其使用场景说的。 建造者模式适用于要创建的对象非常复杂的情况,构造者模式让这个复杂类的装配变得按部就班又直观。
MyBatis 中读取配置文件构建配置类,这个配置类超级大 。所以使用了建造者模式,把这个复杂类的构建拆分成了好多 builder,根据传入的参数的不同进行创建 。
对于原型模式,首先我们需要记住的就是实现 Cloneable 接口,并且重写 clone 方法 。(注意深浅克隆的区别)
其实,按照我对原型模式的理解,就是新建了一个模板。然后,我们不断去克隆这个模版,更改模版中的一些值。