设计模式—创建型

意义:主要解决对象的创建问题,封装复杂的创建过程,解耦对象的创建代码和使用代码

单例模式

概念:一个类只允许创建一个对象(或者实例),那这个类就是一个单例类,这种设计模式就叫作单例设计模式 ​ 应用场景:解决资源访问冲突、创建全局为一类:配置类

单例的5种实现

饿汉式

饿汉式的实现方式,在类加载的期间,就已经将 instance 静态实例初始化好了,所以,instance 实例的创建是线程安全的。不过,这样的实现方式不支持延迟加载实例(在真正用到的时候,再创建实例)。 ​

public class IdGenerator {
private AtomicLong id = new AtomicLong(0);
private static final IdGenerator instance = new IdGenerator();
private IdGenerator() {}  //构造方法                           
public static IdGenerator getInstance() {
return instance;
} //实例方法
public long getId() {
return id.incrementAndGet();
}//实例方法
}

懒汉式

懒汉式相对于饿汉式的优势是支持延迟加载。这种实现方式会导致频繁加锁、释放锁,以及并发度低等问题,频繁的调用会产生性能瓶颈

public class IdGenerator {
private AtomicLong id = new AtomicLong(0);
private static IdGenerator instance;
private IdGenerator() {}
public static synchronized IdGenerator getInstance() {
if (instance == null) {
instance = new IdGenerator();
}
return instance;
}//实例方法
public long getId() {
return id.incrementAndGet();
}//实例方法
}

双重检测

双重检测实现方式既支持延迟加载、又支持高并发的单例实现方式。只要 instance 被创建之后,再调用 getInstance() 函数都不会进入到加锁逻辑中。所以,这种实现方式解决了懒汉式并发度低的问题

public class IdGenerator {
private AtomicLong id = new AtomicLong(0);
private static IdGenerator instance;
private IdGenerator() {}
public static IdGenerator getInstance() {
if (instance == null) {
synchronized(IdGenerator.class) { // 此处为类级别的锁
if (instance == null) {
instance = new IdGenerator();
}
}
}
return instance;
}
public long getId() {
return id.incrementAndGet();
}
}

静态内部类

利用 Java 的静态内部类来实现单例。这种实现方式,既支持延迟加载,也支持高并发,实现起来也比双重检测简单

public class IdGenerator {
private AtomicLong id = new AtomicLong(0);
private IdGenerator() {}
private static class SingletonHolder{
private static final IdGenerator instance = new IdGenerator();
}
public static IdGenerator getInstance() {
return SingletonHolder.instance;
}
public long getId() {
return id.incrementAndGet();
}
}

枚举

最简单的实现方式,基于枚举类型的单例实现。这种实现方式通过 Java 枚举类型本身的特性,保证了实例创建的线程安全性和实例的唯一性

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

单例存在的问题

单例对 OOP 特性的支持不友好 ​ 单例会隐藏类之间的依赖关系 ​ 单例对代码的扩展性不友好 ​ 单例对代码的可测试性不友好 ​ 单例不支持有参数的构造函数

单例的替代解决方案

静态方法/通过工厂模式、IOC 容器(比如 Spring IOC 容器)来保证

单例模式的唯一性

单例类中对象的唯一性的作用范围是“进程唯一”的 ​ 进程唯一指进程内唯一,进程间不唯一,线程内和线程间都唯一

如何实现线程唯一的单例

通过一个 HashMap 来存储对象,其中 key 是线程 ID,value 是对象/java ThreadLocal 并发工具类

如何实现集群环境下的单例

集群唯一”指的是进程内唯一、进程间也唯一 ​ 把这个单例对象序列化并存储到外部共享存储区(比如文件)。进程在使用这个单例对象的时候,需要先从外部共享存储区中将它读取到内存,并反序列化成对象,然后再使用,使用完成之后还需要再存储回外部共享存储区。为了保证任何时刻在进程间都只有一份对象存在,一个进程在获取到对象之后,需要对对象加锁,避免其他进程再将其获取。在进程使用完这个对象之后,需要显式地将对象从内存中删除,并且释放对对象的加锁

如何实现一个多例模式

“多例”指的就是一个类可以创建多个对象,但是个数是有限制的,比如只能创建 3 个对象。 ​ 实现:通过一个Map 来存储对象类型和对象之间的对应关系,来控制对象的个数。

工厂模式

用来创建不同但是相关类型的对象(继承同一父类或者接口的一组子类),由给定的参数来决定创建哪种类型的对象;封装对象的创建过程,将对象的创建和使用相分离 ​ 工厂模式的应用场景:创建逻辑比较复杂,“大工程”的时候 ​ 类似规则配置解析的例子,代码中存在 if-else 分支判断,动态地根据不同的类型创建不同的对象。针对这种情况,我们就考虑使用工厂模式,将这一大坨 if-else创建对象的代码抽离出来,放到工厂类中 ​ 单个对象本身的创建过程比较复杂,比如前面提到的要组合其他类对象,做各种初始化操作。在这种情况下,我们也可以考虑使用工厂模式,将对象的创建过程封装到工厂类中

简单工厂

应用场景:每个对象的创建逻辑都比较简单的时候 ​ 将多个对象的创建逻辑放到一个工厂类中 ​ 实现方式 ​ 每次都返回新创建的对象 ​ 每次都返回同一个事先创建好的对象

工厂方法

应用场景:每个对象的创建逻辑都比较复杂的时候/单个对象本身的创建逻辑就比较复杂 ​ 为了避免设计一个过于庞大的简单工厂类,使用工厂方法模式,将创建逻辑拆分得更细,每个对象的创建逻辑独立到各自的工厂类中

抽象工厂

判断要不要使用工厂模式的标准

封装变化:创建逻辑有可能变化,封装成工厂类之后,创建逻辑的变更对调用者透明 ​ 代码复用:创建代码抽离到独立的工厂类之后可以复用 ​ 隔离复杂性:封装复杂的创建逻辑,调用者无需了解如何创建对象 ​ 控制复杂度:将创建代码抽离出来,让原本的函数或类职责更单一,代码更简洁

DI容器

概念:依赖注入框架,或者叫依赖注入容器 ​ 工厂模式 VS DI容器 ​ DI 容器底层最基本的设计思路就是基于工厂模式的,相当于一个大的工厂类,负责在程序启动的时候,根据配置(要创建哪些类对象,每个类对象的创建需要依赖哪些其他类对象)事先创建好对象,处理的是更大的对象创建工程,负责的是整个应用中所有类对象的创建 ​ 一个工厂类只负责某个类对象或者某一组相关类对象(继承自同一抽象类或者接口的子类)的创建

核心功能

配置解析:将需要由 DI 容器来创建的类对象和创建类对象的必要信息(使用哪个构造函数以及对应的构造函数参数都是什么等等),放到配置文件中。容器读取配置文件,根据配置文件提供的信息来创建对象。 ​ 对象创建:将所有类对象的创建都放到一个工厂类中完成 ​ 对象生命周期管理

实现

核心逻辑:配置文件解析,以及根据配置文件通过“反射”语法来创建对象 ​ 反射语法:能在程序运行的过程中,动态地加载类、创建对象,不需要事先在代码中写死要创建哪些对象 ​ 复习SpringDI、Ioc概念

建造者模式

用来创建复杂对象,可以通过设置不同的可选参数,“定制化”地创建不同的对象。

工厂模式 VS 建造者模式

工厂模式是用来创建不同但是相关类型的对象(继承同一父类或者接口的一组子类),由给定的参数来决定创建哪种类型的对象;类比利用工厂模式,根据用户不同的选择,来制作不同的食物,比如披萨、汉堡、沙拉 ​ 建造者模式是用来创建一种类型的复杂对象,通过设置不同的可选参数,“定制化”地创建不同的对象;类比通过建造者模式根据用户选择的不同配料来制作披萨

应用场景

类的必填的属性有很多,把这些必填属性都放到构造函数中设置,那构造函数就又会出现参数列表很长的问题 ​ 类的属性之间有一定的依赖关系或者约束条件 ​ 需要创建不可变对象

原型模式

概念:如果对象的创建成本比较大,而同一个类的不同对象之间差别不大(大部分字段都相同),在这种情况下,我们可以利用对已有对象(原型)进行复制(或者叫拷贝)的方式,来创建新对象。基于原型来创建对象的方式就叫作原型设计模式,简称原型模式

两种实现方法

浅拷贝:浅拷贝只会复制对象中基本数据类型数据和引用对象的内存地址,不会递归地复制引用对象,以及引用对象的引用对象;主要应用于要拷贝的对象是不可变对象

深拷贝2种实现方式

递归拷贝对象、对象的引用对象以及引用对象的引用对象……直到要拷贝的对象只包含基本数据类型数据,没有引用对象为止 ​ 先将对象序列化,然后再反序列化成新的对象

深拷贝VS浅拷贝

浅拷贝得到的对象跟原始对象(共享数据),而深拷贝得到的是一份完完全全独立的对象 ​ 深拷贝比起浅拷贝来说,更加耗时,更加耗内存空间 ​ 除非操作非常耗时,比较推荐使用浅拷贝,否则,没有充分的理由,不要为了一点点的性能提升而使用浅拷贝

总结

单例设计模式:一个类只允许创建一个对象(或者实例) ​

工厂模式是用来创建不同但是相关类型的对象(继承同一父类或者接口的一组子类),由给定的参数来决定创建哪种类型的对象;类比利用工厂模式,根据用户不同的选择,来制作不同的食物,比如披萨、汉堡、沙拉 ​ ;

建造者模式是用来创建一种类型的复杂对象,通过设置不同的可选参数,“定制化”地创建不同的对象;类比通过建造者模式根据用户选择的不同配料来制作披萨 ​

基于原型来创建对象的方式就叫作原型设计模式

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值