概念:保证一个类仅有一个实例,并提供一个访问它的全局访问点
饿汉在类加载时就完成了初始化,避免了多线程同步的问题,如果始终没有使用这个实例,就会造成内存浪费。
懒汉模式声明了一个静态对象,在用户第一次调用时初始化。在多线程时不能正常工作。
这种写法在多线程中可以很好的工作,但是每次调用getInstance方法都需要同步,造成不必要的时间开销。
在方法内部进行了两次判空,第一次为了不必要的同步,第二次是在实例等于null的时候才创建,但是这样会出现DCL问题,如下图
创建对象时会出现指令重排。
指令重排序可以保证串行语义一致,但是没有义务保证多线程间的语义也一致。也就是说上面3行伪代码的2和3之间虽然被重排序了,但是是不影响串行语义的。但是在多线程并发执行的情况就可能出现:
当一个线程创建对象出现了指令重排,他执行了步骤1、3,此时另一个线程过来取单例去使用,但是这时对象创建的初始化工作还没有做,所有就会出现问题。
解决:使用volatile修饰对象
第一次加载Singleton时,并不会初始化sInstance,只有在第一次调用getInstance方法时虚拟机加载SingletonHolder并初始化SInstance。这样保证了线程安全,也能保证Singleton类的唯一性。
上面的单例模式中有一种情况会重新创建对象——反序列化
将一个单例实例对象写到磁盘再读回来,从而获得了一个实例。
反序列化操作提供了readResolve方法,这个方法可以让开发人员控制对象的反序列化。在上述几个方法示例中,如果要杜绝单例对象被反序列化时重新生成对象。就需要加入如上代码。
class SingletonManager {
private static Map<String, Object> objectMap = new HashMap<>();
private SingletonManager() {}
public static void registerService(String key, Object instance) {
if (!objectMap.containsKey(key)) {
objectMap.put(key, instance);
}
}
public static Object getService(String key) {
return objectMap.get(key);
}
}
将多种单例类注入到一个统一的管理类中,在使用是通过key获取对应类型的对象。
在一个系统中,要求一个类有且仅有一个对象,它的具体使用场景如下:
又叫静态工厂方法模式。
简单工厂模式,其属于创建型设计模式,但是并不属于 23种GoF设计模式之一。提到它是为了让大家能够更好地理解后面讲到的工厂方法模式。
在简单工厂模式中有如下角色。
我们用生产计算机来举例,假设有一个计算机的代工生产商,它目前已经可以代工生产联想计算机了。随着业务的拓展,这个代工生产商还要生产惠普和华硕的计算机。这样我们就需要用一个单独的类来专门生产计算机,这就用到了简单工厂模式。下面我们来实现简单工厂模式。
创建各个品牌的计算机,其都继承了自己的父类Computer,并实现了父类的start方法。具体的计算机产品分别是联想、惠普和华硕计算机:
工厂类
创建一个工厂类,它提供了一个静态方法 createComputer 用来生产计算机。你只需要传入自己想生产的计算机的品牌,它就会实例化相应品牌的计算机对象,代码如下所示:
客户端调用
客户端调用工厂类,传入"hp"生产出惠普计算机并调用该计算机对象的 start 方法
使用场景
优点
缺点
定义一个用于创建对象的接口,让子类决定实例化哪个类。工厂方法使一个类的实例化延迟到其子类。
工厂方法模式中有以下角色:
工厂方法模式的抽象产品类与具体产品类的创建和简单工厂模式是一样的,具体在上面简单工厂。
创建抽象工厂:
抽象工厂里面有一个 createComputer 方法,想生产哪个品牌的计算机就生产哪个品牌的。
广达代工厂是一个具体的工厂,其继承抽象工厂,通过反射来生产不同厂家的计算机:
客户端创建了GDComputerFactor,并分别生产了联想、惠普和华硕计算机:
在任何需要生成复杂对象的地方,都可以使用工厂方法模式。复杂对象适合使用工厂模式,用new就可以完成创建的对象无需使用工厂模式。
对于简单工厂,我们在工厂类中进行了必要的逻辑判断,根据不同的条件实例化相关的类。
除去了客户端与具体产品的依赖,但是带来了一个问题:如果我们需要添加产品就需要修改我们的工厂类,这违背了开放封闭原则。
而工厂方法模式就没有违背这个开放封闭原则。如果我们需要生产苹果计算机,则无须修改工厂类,直接创建产品即可。
将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
它允许用户不知道内部构建细节的情况下,可以精细的控制构造流程。
在建造者模式中有如下角色
用DIY组装计算机的例子来实现一下建造者模式。
创建产品类
组装一台计算机,计算机被抽象为Computer类,它有3个部件:CPU、主板和内存,并在里面提供了3个方法分别用来设置CPU、主板和内存:
商家组装计算机有一套组装方法的模板,就是一个抽象的 Builder 类,其里面提供了安装CPU、主板和内存的方法,以及组装成计算机的create方法
商家实现了抽象的Builder类,MoonComputerBuilder类用于组装计算机
用导演类来统一组装过程
商家的导演类用来规范组装计算机的流程规范,先安装主板,再安装CPU,最后安装内存并组装成计算机:
客户端调用导演类
最后商家用导演类组装计算机。我们只需要提供自己想要的CPU、主板和内存就可以了,至于商家怎样组装计算机我们无须知道
使用场景
优点
缺点