• 工厂模式和单例模式


    目录

    一.设计模式

    二.工厂模式和单例模式

    🐞单例模式

    1.饿汉模式 

    2.懒汉模式

    (1)问题一:大量对象的创建

    (2)问题二:加锁导致阻塞等待

    (3)问题三:指令重排序

    🐞工厂模式

     三.总结分析


    一.设计模式

    设计模式

    设计模式是一种被广泛接受和验证的解决问题的经验总结,它提供了一套通用的解决方案,帮助开发人员解决常见的设计问题。使用设计模式可以提高代码的可读性、可维护性和可扩展性,减少代码的重复性,提高开发效率。🤗

    学习设计模式的意义主要体现在以下几个方面:

    1. 提高设计能力:学习设计模式可以帮助开发人员理解和掌握各种设计原则和设计思想,提高设计能力,能够更好地进行系统设计和架构设计。

    2. 代码重用和维护:设计模式提供了一套通用的解决方案,可以避免重复造轮子,提高代码的重用性。同时,设计模式的使用也能够提高代码的可读性和可维护性,使代码更易于理解和修改。

    3. 提高团队合作效率:设计模式是一种标准化的解决方案,团队成员之间可以通过共享和理解设计模式来进行沟通和合作,提高团队的合作效率。

    4. 更好地理解和应用框架:许多框架和库都使用了设计模式,学习设计模式可以帮助开发人员更好地理解和应用这些框架,提高开发效率。

    我们今天来详细了解面试常考的工厂模式和单例模式👀

    二.工厂模式和单例模式

    🐞单例模式

    单例,顾名思义,也就是单个实例(instance),类的实例,就是对象

    单例,也就是在一个程序中的某个类,只创建出一个实例(一个对象),不能创建多个对象

    ⌛有的人可能会想,那我多new几次,不就创建出多个对象了吗?🤔

    但是语法上是有办法禁止让你这样多new的.

    Java中的单例模式,实际上就是借助Java语法,保证某个类,只能够创建出一个实例,而不能多次"new"

    ⌛那么单例模式用途是什么呢?

    Java中的有些场景,本身就是要求某个概念是单例的

    比如只能有一个太阳,一个人只能有一个妻子.

    接下来,我们看看在Java中如何实现单例模式.🤗

    实际上,Java中实现单例模式有很多种写法,我们主要来学习这两种:

    • 饿汉模式 
    • 懒汉模式

     我们来看这段代码:

    1. class Singleton{
    2. //唯一实例的本体
    3. private static Singleton instance =new Singleton();
    4. //获取实例的方法
    5. public static Singleton getInstance(){
    6. return instance;
    7. }
    8. //禁止外部new对象
    9. private Singleton(){
    10. }
    11. }
    12. public class ThreadDemo17 {
    13. public static void main(String[] args) {
    14. //此时s1和s2调用得到的是同一个对象
    15. Singleton s1=Singleton.getInstance();
    16. Singleton s2=Singleton.getInstance();
    17. //Singleton s3=new Singleton();
    18. }
    19. }

    我们来具体分析这段代码:

    📢饿汉模式,突出一个词是"急迫"

    📢懒汉模式,形容起来就是"从容"

    具体是怎么回事呢?我们来进行详细介绍

    懒汉模式来实现单例.主要的核心思想就是非必要,不创建

    而饿汉就是提前创建好,直接可以使用

    1.饿汉模式 

    代码如下:

    1. //饿汉模式
    2. class Singleton{
    3. //唯一实例的本体
    4. private static Singleton instance =new Singleton();
    5. //获取实例的方法
    6. public static Singleton getInstance(){
    7. return instance;
    8. }
    9. //禁止外部new对象
    10. private Singleton(){
    11. }
    12. }

    2.懒汉模式

    1. //懒汉模式
    2. class SingletonLazy{
    3. public static SingletonLazy instance=null;
    4. public static SingletonLazy getInstance(){
    5. if(instance==null){
    6. instance=new SingletonLazy();
    7. }
    8. return instance;
    9. }
    10. private SingletonLazy(){
    11. }
    12. }

    上面的代码并不难,但是有几个重要问题:

    ⌛以上两个代码,是否是线程安全的?

    ⌛多个线程下调用getInstance,是否会出现问题?

    ⌛代码中加上了synchorized,是为什么呢?

    饿汉模式认为线程是安全的,因为只是读数据

    但是懒汉模式在多线程下,却无法保证创建对象的唯一性.

    (1)问题一:大量对象的创建

    我们可以看到,懒汉模式需要进行判空,那么如果在空对象条件下突然有多个线程进行调用,就有可能创建出多个对象,这个时候就有可能会出现问题了.

    这是因为对象有大有小,有些对象管理的内存数据可能会有很多,如果同时调用创建出这么多对象,线程就有可能会崩溃了.

    所以,我们如何解决上述问题呢?办法就是加锁.

    代码如下:

    1. //懒汉模式
    2. class SingletonLazy{
    3. public static SingletonLazy instance=null;
    4. public static SingletonLazy getInstance(){
    5. if(instance==null){
    6. synchronized (SingletonLazy.class){
    7. instance=new SingletonLazy();
    8. }
    9. }
    10. return instance;
    11. }
    12. private SingletonLazy(){
    13. }
    14. }

    这样加上锁之后就能保证判定和new是一个原子操作,这样就可以避免同时创建出大量对象了.

    (2)问题二:加锁导致阻塞等待

    然而,加锁是一个比较低效的操作,因为加锁了就可能涉及到阻塞等待,所以我们的原则是非必要不加锁,那么,还有什么更好的办法吗?

    我们可以修改代码形式以达到避免阻塞等待的目的。

    我们来看看这个代码:

    1. class SingletonLazy{
    2. private static SingletonLazy instance=null;
    3. public static SingletonLazy getInstance(){
    4. if(instance==null){
    5. synchronized (SingletonLazy.class){
    6. if(instance==null){
    7. instance=new SingletonLazy();
    8. }
    9. }
    10. }
    11. return instance;
    12. }
    13. private SingletonLazy(){
    14. }
    15. }

    我们来对代码进行分析:

    (3)问题三:指令重排序

    但是,事情到这里就结束了吗?

    上述代码其实还面临着一个很重要的问题——指令重排序

     那么,这个问题如何来解决呢?

    我们可以通过添加volatile来禁止指令重排序

    具体代码如下:

    1. class SingletonLazy{
    2. volatile private static SingletonLazy instance=null;
    3. public static SingletonLazy getInstance(){
    4. if(instance==null){
    5. synchronized (SingletonLazy.class){
    6. if(instance==null){
    7. instance=new SingletonLazy();
    8. }
    9. }
    10. }
    11. return instance;
    12. }
    13. private SingletonLazy(){
    14. }
    15. }

    这样,就可以有效解决上述问题了

    我们现在可以来总结一下了

    单例模式具有线程安全问题

    懒汉模式:天然就是安全的,只是读操作

    饿汉模式:不安全,有读和写操作

    解决方法:1.加锁,把if和new变成原子操作

                    2.双重if,减少不必要的加锁操作

                    3.使用volatile禁止指令指令重排序,保证后续线程拿到的是完整对象.

    🐞工厂模式

    工厂模式(Factory Pattern)是一种创建型设计模式,它提供了一种创建对象的最佳方式。

    在工厂模式中,我们创建对象而不是直接通过 new 关键字来创建,而是使用一些其他的方法(通常是静态方法)帮助我们把对象创建出来.

    工厂模式,实际上是用来填构造方法的坑的.因为构造方法如果想要提供多种不同的构造对象的方法,就得基于重载.

    比如说我们想描述平面上的一个点,就有很大的问题.

     这个时候,我们就可以通过构造一个工厂类来解决.

    代码如下:

    1. public class PointBuilder {
    2. // 根据坐标创建 Point 对象
    3. public static Point makePointByXY(double x, double y) {
    4. return new Point(x, y);
    5. }
    6. // 根据极坐标创建 Point 对象
    7. public static Point makePointByRA(double r, double a) {
    8. double x = r * Math.cos(a);
    9. double y = r * Math.sin(a);
    10. return new Point(x, y);
    11. }
    12. }

    在上面的代码中,我们定义了一个工厂类 PointBuilder,其中包含了两个静态方法 makePointByXY 和 makePointByRA。这两个方法分别根据坐标和极坐标创建 Point 对象。

    📢makePointByXY 方法接收两个参数 x 和 y,直接使用这两个参数创建一个 Point 对象并返回。

    📢makePointByRA 方法接收两个参数 r 和 a,根据极坐标的公式 x = r * cos(a) 和 y = r * sin(a) 计算出 x 和 y 的值,然后使用这两个值创建一个 Point 对象并返回。

    这样,调用者可以直接通过 PointFactory 类来创建 Point 对象,而不需要关心对象的创建细节。调用者可以根据需要选择使用哪个方法来创建对象。

     三.总结分析

    单例模式和工厂模式是两种常见的设计模式,它们在软件开发中具有不同的作用和应用场景。

    • 单例模式用于确保一个类只有一个实例存在,并提供全局访问点。它适用于需要全局共享某个对象的场景,例如资源池、配置信息等。

    • 工厂模式用于封装对象的创建过程,将对象的实例化和使用解耦。它适用于需要根据不同的条件创建不同的对象的场景,提供灵活性和可扩展性。

     在以后的学习中,应根据具体的需求和设计目标选择适合的设计模式..🤗

  • 相关阅读:
    爆肝整理,Jmeter接口性能测试-跨线程调用变量实操(超详细)
    想要精通算法和SQL的成长之路 - 最长序列问题
    代码随想录算法训练营第三十九天 | 动态规划part02
    功率信号源有哪些波形类型
    点云从入门到精通技术详解100篇-基于3D点云的曲面文字检测
    lowbit和树状数组的理解与部分应用
    如何使用HTML制作个人网站(如何搭建个人博客)
    生产环境日志打印console.log内存溢出解决方法
    【全网最全】springboot整合JSR303参数校验与全局异常处理
    ROS2系列知识(4): 理解【服务】的概念
  • 原文地址:https://blog.csdn.net/LxinY213/article/details/133389271