『设计模式』创建型设计模式

本文深入讲解了简单工厂、工厂方法、抽象工厂及单例模式。分析了每种模式的优缺点,探讨了如何在实际开发中应用这些模式,特别是在解决开闭原则方面。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、简单工厂模式

1.1 介绍

简单工厂模式有三个角色,工厂类根据参数不同创建不同的实例,实例具有共同的父类。

image-20211217182132240

1.2 缺点

image-20211217183927303

1、创建产品对象,需要更换产品则需修改静态工厂方法的参数,违反开闭原则

chart = ChartFactory.getChart("柱状图");
chart = ChartFactory.getChart("条形图");

2、会增加类的个数。

3、拓展困难,增加新产品,需修改工厂逻辑。

4、静态工厂方法,使角色不能形成继承结构。

1.3 优点

1、对象创建和使用的分离。

2、客户端不需要知道具体产品类名,记住参数即可。

3、通过 XML 可以无需修改客户端代码,增减具体的产品类。

二、工厂方法模式

2.1 介绍

工厂方法模式有四个角色,工厂方法模式在简单工厂模式上,弥补了开闭原则,增加新产品类无需修改工厂逻辑。

定义一个创建对象的接口,让子类决定实例化谁,工厂方法让类的实例化延迟到子类

image-20211217185747354

2.2 优点

1、只需关注具体工厂,无需知道具体产品类名。

2、工厂角色和产品角色的多态性。

3、新增产品无需修改任何东西,只需增加一个具体工厂和具体产品即可,符合开闭原则。

2.3 缺点

1、新增产品需要写新的具体产品和具体工厂类,类个数增加

2、可拓展性基于抽象层,增加系统抽象性和理解程度。

三、抽象工厂模式

3.1 介绍

工厂方法模式有四个角色,提供一个创建相关对象的接口,无需指定具体的类。比工厂方法模式,抽象程度更高。工厂方法的具体工厂只生产一种具体产品,而抽象工厂的具体工厂可以生产一组相关产品(产品族)。

image-20211217194759181

3.11 产品族

引入工厂等级结构, ①解决简单工厂模式的工厂类职责太重问题。②工厂方法模式的具体工厂只生产一种产品,系统会存在大量(具体)工厂类,将相关产品(产品族)由一个工厂类生产。

image-20211217193724316

3.2 优点

1、具体工厂都实现了抽象工厂方法,修改具体工厂实例,可以更改软件整个行为。

2、产品族中多个对象一起工作时,保证使用的是同一个产品族。

3、增加产品族方便,符合开闭原则。

3.3 缺点

1、增加产品族很简单,新增具体工厂即可;增加产品等级结构很麻烦,需要修改抽象工厂和所有具体工厂。[开闭原则倾斜性]

四、单例模式

4.1 介绍

image-20211217201729542

1、确保一个类只有一个实例,并提供一个访问它的全局访问点。

2、自行创建这个实例,私有构造函数禁止用户 new 实例化它。

3、自行向系统提供这个实例。

image-20211217202132656

4.2 饿汉式单例与懒汉式单例

4.21 饿汉式单例

image-20211217202551123

类加载时,静态变量 instance 被初始化,调用构造方法,实例化成功。[饥渴的表现]

4.22 懒汉式单例与双重检查锁定

image-20211217203249828

在调用 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、实例化共享对象长期不使用,会被回收。下次使用时又重新实例化,导致共享单例对象丢失。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值