文章目录
1、简单工厂模式
1.1 介绍
简单工厂模式有三个角色,工厂类根据参数不同创建不同的实例,实例具有共同的父类。
1.2 缺点
1、创建产品对象,需要更换产品则需修改静态工厂方法的参数
,违反开闭原则
。
chart = ChartFactory.getChart("柱状图");
chart = ChartFactory.getChart("条形图");
2、会增加类的个数。
3、拓展困难,增加新产品,需修改工厂逻辑。
4、静态工厂方法,使角色不能形成继承结构。
1.3 优点
1、对象创建和使用的分离。
2、客户端不需要知道具体产品类名,记住参数即可。
3、通过 XML 可以无需修改客户端代码,增减具体的产品类。
二、工厂方法模式
2.1 介绍
工厂方法模式有四个角色,工厂方法模式在简单工厂模式上,弥补了开闭原则
,增加新产品类无需修改工厂逻辑。
定义一个创建对象的接口,让子类决定实例化谁,工厂方法让类的实例化延迟到子类
。
2.2 优点
1、只需关注具体工厂,无需知道具体产品类名。
2、工厂角色和产品角色的多态性。
3、新增产品无需修改任何东西,只需增加一个具体工厂和具体产品即可,符合开闭原则。
2.3 缺点
1、新增产品需要写新的具体产品和具体工厂类,类个数增加
。
2、可拓展性基于抽象层,增加系统抽象性和理解程度。
三、抽象工厂模式
3.1 介绍
工厂方法模式有四个角色,提供一个创建相关对象的接口,无需指定具体的类。比工厂方法模式,抽象程度更高
。工厂方法的具体工厂只生产一种
具体产品,而抽象工厂的具体工厂可以生产一组
相关产品(产品族)。
3.11 产品族
引入工厂等级结构
, ①解决简单工厂模式的工厂类职责太重问题。②工厂方法模式的具体工厂只生产一种产品,系统会存在大量(具体)工厂类,将相关产品(产品族)由一个工厂类生产。
3.2 优点
1、具体工厂都实现了抽象工厂方法,修改具体工厂实例,可以更改软件整个行为。
2、产品族中多个对象一起工作时,保证使用的是同一个产品族。
3、增加产品族方便,符合开闭原则。
3.3 缺点
1、增加产品族很简单,新增具体工厂即可;增加产品等级结构很麻烦
,需要修改抽象工厂和所有具体工厂。[开闭原则倾斜性]
四、单例模式
4.1 介绍
1、确保一个类只有一个实例
,并提供一个访问它的全局访问点。
2、自行创建这个实例,私有构造函数
禁止用户 new 实例化它。
3、自行向系统提供这个实例。
4.2 饿汉式单例与懒汉式单例
4.21 饿汉式单例
类加载时
,静态变量 instance 被初始化,调用构造方法,实例化成功
。[饥渴的表现]
4.22 懒汉式单例与双重检查锁定
在调用 getInstance 方法时,才会实例化。加上关键字 synchronized
,任何时刻只有一个线程可调用该方法,解决了线程安全问题。
但是每次调用方法时都会进行线程锁定判断,在高并发环境中导致性能降低。因此,可对懒汉单例改造,我们发现无需锁定整个方法,只需锁定 instance = new LazySingleton()
。
......
public static LazySingleton getInstance() {
if (instance == null) {
// 仅锁定实例化方法
synchronized (LazySingleton.class) {
instance = new LazySingleton();
}
}
return instance;
}
看似性能问题得到解决,但会存在单例对象不唯一情况
。
比如:同一时刻A、B同时调用 getInstance,此时 A、B 均能通过 instance == null
的判断,A 开始实例化加锁,B 则等待。当 A 创建实例成功释放锁,此时 B 不知道已经实例化了,也会创建新的实例。
所以需要在 synchronized
中再次判断对象是否已被实例化。
注意:静态成员变量 instance 需要加上 volatile
修饰,保证多个线程正常处理。
public class LazySingleton {
private volatile LazySingleton instance = null;
private LazySingleton(){};
public static LazySingleton getInstance() {
// 一重判断
if (instance == null) {
// 仅锁定实例化方法
synchronized (LazySingleton.class) {
// 二重判断
if(instance == null) {
instance = new LazySingleton();
}
}
}
return instance;
}
}
4.23 区别
饿汉模式:
- 类加载时实例化,无需考虑多线程同时访问。
- 调用速度比懒汉模式块。
- 资源利用率不及懒汉模式。
- 系统创建时,创建饿汉对象,加载时间较长
懒汉模式:
- 第一次使用时创建,延迟加载。
- 需要考虑多线程同时访问,需要双重检查锁定,性能会受影响。
4.24 静态内部类实现单例模式
饿汉单例不能延迟加载,不管用不用都占用内存;懒汉单例安全控制繁琐,性能受影响。
无论哪个都有问题,在 Java 中可通过 IoDH
实现单例模式。
在单例类中增加静态内部类
,在内部类中创建单例对象,再通过 getInstance 返回给外部。
public class Singleton {
private Singleton(){};
// 静态内部类
private static class HolderClass {
private final static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return HolderClass.instance;
}
}
由于 instance 不是 Singleton 的成员变量,类加载时不会实例化。getInstance 方法无线程锁定,故性能无影响。
通过 IoDH
实现延迟加载,和线程安全,最好的单例实现方式
。但很多面向对象语言不支持 IoDH。
4.3 优点
1、唯一访问实例。
2、内存只存在一个对象,节约资源。对于频繁创建销毁的对象,单例模式可提供系统性能。
3、允许可变实例的数目。
4.4 缺点
1、无抽象层,单例类拓展困难。
2、单例类职责过重,违背单一职责原则。
3、实例化共享对象长期不使用,会被回收。下次使用时又重新实例化,导致共享单例对象丢失。