设计模式学习2 创建者模式
创建者模式
创建者模式的主要关注点是“怎样创建对象”,主要特点是“将对象的创建与使用分离”
这样可以降低系统的耦合度,使用者不关心对象的创建细节
创建者模式可以分为:
- 单例模式
- 工厂模式
- 抽象工程模式
- 原型模式
- 建造者模式
单例设计模型
涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建,这个类提供了访问其唯一对象的方式,可以直接访问,不需要实例化该类的对象
单例模式的结构
单例模式主要有以下角色
- 单例类:只能创建一个实例的类
- 访问类:使用单例的类
单例设计模式分为两种
- 饿汉式:类加载就会导致该单实例对象被创建
- 懒汉式:类加载不会导致该单例实例对象被创建,而是首次使用该对象时才会创建
1.饿汉式
- 方式一:静态变量方式
- 方式二:静态代码块方式
方式一:
public class Singleton {
//1.私有构造方法
private Singleton() {}
//2.在本类中创建本类对象
private static Singleton instance = new Singleton();
//3.提供一个公共的访问方式
public static Singleton getInstance() {
return instance;
}
}
方式二:
public class Singleton {
private Singleton() {}
//声明singleton类型的变量
private static Singleton instance;
static {
instance = new Singleton()
}
//对外提供访问方式
public static Singleton getInstance() {
return instance;
}
}
说实话,这两个没啥区别
枚举方式:
public enum Singleton {
INSTANCE;
}
优点:线程安全。只会被装载一次,是唯一一种不会被破坏的单例实现模式
2.懒汉式
1.线程安全方式
public class Singleton {
private Singleton() {
}
//声明singleton类型的变量instance
private static Singleton instance;
//为了保证线程安全
public static synchronized Singleton getInstance() {
//判断instance是否为null,如果为null,说明没有创建singleton类的对象
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
2.双重检查锁
public class Singleton {
private Singleton() {
}
private static Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
3.静态内部类
public class Singleton {
private Singleton() {
}
//定义一个静态内部类
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
//提供公共访问方式
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
这种方法是一种比较优秀的单例模式,在没有加任何锁的情况下,保证了多线程下的安全,并且没有任何性能影响和空间的浪费
单例模式存在的问题
通过序列化和反射可以破坏上面的实现的单例模式
序列化与反序列化破坏单例模式
代码示例
public class Client {
public static void main(String[] args) throws Exception {
//序列化破坏单例模式
// writeObject2File();
readObjectFromFile();
readObjectFromFile();
}
//向文件中写数据
public static void writeObject2File() throws Exception {
Singleton instance = Singleton.getInstance();
//创建对象输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(".\\a.txt"));
//写对象
oos.writeObject(instance);
//释放资源
oos.close();
}
//从文件中读数据
public static void readObjectFromFile() throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(".\\a.txt"));
//读取对象
Singleton instance = (Singleton) ois.readObject();
System.out.println(instance);
//释放资源
ois.close();
}
}
反射破坏单例模式
public class Client {
//反射破坏单例模式
public static void main(String[] args) throws Exception {
//获取字节码
Class<Singleton> clazz = Singleton.class;
//获取无参构造方法对象
Constructor<Singleton> cons = clazz.getDeclaredConstructor();
//取消访问检查
cons.setAccessible(true);
//创建instance对象
Singleton singleton1 = cons.newInstance();
Singleton singleton2 = cons.newInstance();
System.out.println(singleton2 == singleton1);
}
}
解决序列化破坏单例模式的方法
在singleton中添加readResolve方法,在反序列化时被反射调用
@Serial
public Object readResolve() {
return SingletonHolder.INSTANCE;
}
解决反射破坏单例模式的方法
private Singleton() {
synchronized (Singleton.class) {
//判断flag值是否是true
//是true说明非第一次访问,直接抛一个异常,如果是false,则说明是第一次访问
if (flag) {
throw new RuntimeException("不能创建多个对象");
}
flag = true;
}
}
工厂模式
我们使用工厂来生产对象,我们只需要和工厂打交道,和对象解耦,如果要更换对象,直接在工厂里更换对象即可,达到了与对象解耦的目的,工厂模式最大的优点就是解耦
工厂模式有三个模式
- 简单工厂模式
- 工厂方法模式
- 抽象工厂模式
简单工厂模式
严格来说不算是一种设计模式,更像是一种编程习惯
结构
- 抽象产品:定义了产品的规范,描述了产品的主要特征和功能
- 具体产品:实现或继承抽象产品的子类
- 具体工厂:提供了创建产品的方法,调用者通过该方法来获取产品
优缺点
优点:将对象的创建和业务逻辑层分开,这样以后就避免了修改客户代码,实现新产品就可以直接修改工厂类,而不需要在原代码中修改,这样就降低了客户代码修改的可能性,更加容易扩展
缺点:只是把耦合转移到了工厂,没有真正的解决耦合
静态工厂模式
在开发中也有一部分人将工厂类中的创建对象的功能定义为静态的,这个就是静态工厂模式,调用的时候就不用创建工厂对象了
这个其实也不在23种设计模式
工厂方法模式
结构
- 抽象工厂:提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品
- 抽象产品:定义了产品的规范,描述了产品的主要特征和功能
- 具体产品:实现或继承抽象产品的子类
- 具体工厂:提供了创建产品的方法,调用者通过该方法来获取产品
优缺点
优点
- 用户只需要知道具体工厂名称就可得到所要的产品,无须知道产品的具体创建过程
- 在系统添加新的产品时只需要添加具体产品类和对应的具体工厂类,无需对原工厂进行修改,满足开闭原则
缺点
- 会增加系统的复杂度
抽象工厂模式
抽象工厂模式将考虑多等级产品的生产,将同一个具体工厂所生产的位于不同等级的一组产品称为一个产品族
抽象工厂模式是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无需指定所要产品的具体类就能够得到同族的不同等级的产品的模式结构
是工厂方法模式的升级版本
结构
- 抽象工厂:提供了创建产品的接口,包含多个创建产品的方法,可以创建多个不同等级的产品
- 抽象产品:定义了产品的规范,描述了产品的主要特征和功能
- 具体产品:实现了抽象产品角色所定义的接口
- 具体工厂:实现抽象工厂中的多个抽象方法,完成具体产品的创建
如果添加一个产品族的话,只需要再加一个对应的工厂类即可,不需要修改其他的类
优缺点
优点:
能保证客户端始终使用同一个产品族中的对象
缺点:
当产品族中需要新增一个新的产品时,所有的工厂类都需要进行修改
模式扩展
简单工厂+配置文件解除耦合
可以通过工厂模式+配置文件的方式解除工厂对象和产品对象的耦合,在工厂类中加载配置文件的全类名,并创建对象进行存储,客户端如果需要对象,直接获取即可
静态成员变量用来储存创建的对象,而读取配置文件以及创建对象写在静态代码块中,目的是只需执行一次