设计模式详解:单例模式、工厂方法模式、抽象工厂模式

设计模式详解:单例模式、工厂方法模式、抽象工厂模式

目录

  1. 单例模式
  2. 工厂方法模式
  3. 抽象工厂模式
  4. 三种模式对比总结
  5. 面试高频点总结

单例模式

简介

单例模式(Singleton Pattern)是一种创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点。单例模式的核心思想是控制实例的创建过程,保证系统中只有一个实例存在。

核心流程

  1. 私有化构造函数:防止外部直接创建实例
  2. 提供静态方法:作为全局访问点
  3. 延迟初始化:在需要时才创建实例
  4. 线程安全:确保多线程环境下的安全性
单例模式流程图

是 否 是 否 存在 不存在 客户端请求实例 实例是否已存在? 返回现有实例 是否线程安全? 创建新实例 加锁 再次检查实例 返回现有实例 创建新实例 释放锁 返回新实例 使用实例

实现方式

单例模式实现方式对比流程图

单例模式实现方式 饿汉式 懒汉式 双重检查锁定 静态内部类 枚举 类加载时创建实例 线程安全 可能浪费内存 延迟创建实例 非线程安全 需要加锁优化 延迟创建实例 双重检查保证线程安全 volatile防止重排序 延迟创建实例 JVM保证线程安全 推荐使用 类加载时创建实例 JVM保证线程安全 防止反射攻击

1. 饿汉式(线程安全)
java 复制代码
public class EagerSingleton {
    private static final EagerSingleton instance = new EagerSingleton();
  
    private EagerSingleton() {}
  
    public static EagerSingleton getInstance() {
        return instance;
    }
}
2. 懒汉式(非线程安全)
java 复制代码
public class LazySingleton {
    private static LazySingleton instance;
  
    private LazySingleton() {}
  
    public static LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}
3. 双重检查锁定(线程安全)
java 复制代码
public class DoubleCheckSingleton {
    private static volatile DoubleCheckSingleton instance;
  
    private DoubleCheckSingleton() {}
  
    public static DoubleCheckSingleton getInstance() {
        if (instance == null) {
            synchronized (DoubleCheckSingleton.class) {
                if (instance == null) {
                    instance = new DoubleCheckSingleton();
                }
            }
        }
        return instance;
    }
}
4. 枚举单例(推荐)
java 复制代码
public enum EnumSingleton {
    INSTANCE;
  
    public void doSomething() {
        // 业务逻辑
    }
}

重难点分析

重难点1:线程安全问题
  • 问题:多线程环境下可能创建多个实例
  • 解决方案
    • 使用synchronized关键字
    • 使用volatile关键字防止指令重排序
    • 使用枚举实现(JVM保证线程安全)
重难点2:序列化问题
  • 问题:反序列化时可能创建新实例
  • 解决方案:实现readResolve()方法
java 复制代码
private Object readResolve() {
    return instance;
}
重难点3:反射攻击
  • 问题:通过反射可以绕过私有构造函数
  • 解决方案:在构造函数中检查实例是否已存在
java 复制代码
private LazySingleton() {
    if (instance != null) {
        throw new RuntimeException("单例模式被破坏");
    }
}

Spring中的源码分析

ApplicationContext的单例实现
java 复制代码
// AbstractApplicationContext.java
public abstract class AbstractApplicationContext extends DefaultResourceLoader
        implements ConfigurableApplicationContext {
  
    // 单例Bean的缓存
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
  
    @Override
    public Object getBean(String name) throws BeansException {
        return doGetBean(name, null, null, false);
    }
  
    protected <T> T doGetBean(String name, @Nullable Class<T> requiredType,
            @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
    
        // 先从单例缓存中获取
        Object sharedInstance = getSingleton(name);
        if (sharedInstance != null && args == null) {
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
        }
        // ... 其他逻辑
    }
}
DefaultSingletonBeanRegistry
java 复制代码
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
  
    // 单例Bean的缓存
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
  
    // 单例工厂的缓存
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
  
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }
}

具体使用场景

  1. 配置管理器:全局配置信息
  2. 数据库连接池:避免重复创建连接
  3. 日志记录器:统一日志输出
  4. 缓存管理器:全局缓存控制
  5. 线程池:资源池管理

工厂方法模式

简介

工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它定义了一个创建对象的接口,但让子类决定实例化哪一个类。工厂方法让类的实例化推迟到子类中进行。

核心流程

  1. 定义抽象产品接口:声明产品的通用方法
  2. 定义具体产品类:实现产品接口
  3. 定义抽象工厂接口:声明创建产品的方法
  4. 定义具体工厂类:实现创建具体产品的逻辑
  5. 客户端调用:通过工厂创建产品
工厂方法模式流程图

产品层次 工厂层次 具体产品A 抽象产品接口 具体产品B 具体工厂A 抽象工厂接口 具体工厂B 创建产品A 创建产品B 客户端 选择具体工厂 调用工厂方法 工厂创建产品 返回具体产品 客户端使用产品

实现示例

产品接口和实现
java 复制代码
// 抽象产品
public interface Product {
    void use();
}

// 具体产品A
public class ConcreteProductA implements Product {
    @Override
    public void use() {
        System.out.println("使用产品A");
    }
}

// 具体产品B
public class ConcreteProductB implements Product {
    @Override
    public void use() {
        System.out.println("使用产品B");
    }
}
工厂接口和实现
java 复制代码
// 抽象工厂
public interface ProductFactory {
    Product createProduct();
}

// 具体工厂A
public class ConcreteProductAFactory implements ProductFactory {
    @Override
    public Product createProduct() {
        return new ConcreteProductA();
    }
}

// 具体工厂B
public class ConcreteProductBFactory implements ProductFactory {
    @Override
    public Product createProduct() {
        return new ConcreteProductB();
    }
}
客户端使用
java 复制代码
public class Client {
    public static void main(String[] args) {
        ProductFactory factoryA = new ConcreteProductAFactory();
        Product productA = factoryA.createProduct();
        productA.use();
    
        ProductFactory factoryB = new ConcreteProductBFactory();
        Product productB = factoryB.createProduct();
        productB.use();
    }
}

重难点分析

工厂方法模式演进流程图

抽象工厂模式 工厂方法模式 简单工厂模式 具体工厂类 抽象工厂接口 每个工厂创建产品族 支持产品族扩展 具体工厂类 抽象工厂接口 每个工厂创建一种产品 符合开闭原则 创建所有产品 一个工厂类 违反开闭原则 简单工厂模式 工厂方法模式 抽象工厂模式

重难点1:开闭原则
  • 问题:如何在不修改现有代码的情况下添加新产品
  • 解决方案:通过添加新的具体工厂类来支持新产品
重难点2:依赖倒置
  • 问题:高层模块不应该依赖低层模块
  • 解决方案:依赖抽象而不是具体实现
重难点3:工厂选择策略
  • 问题:如何选择合适的工厂
  • 解决方案
    • 配置文件驱动
    • 策略模式结合
    • 简单工厂模式结合

Spring中的源码分析

BeanFactory接口
java 复制代码
public interface BeanFactory {
    Object getBean(String name) throws BeansException;
    <T> T getBean(String name, Class<T> requiredType) throws BeansException;
    <T> T getBean(Class<T> requiredType) throws BeansException;
    Object getBean(String name, Object... args) throws BeansException;
    // ... 其他方法
}
DefaultListableBeanFactory
java 复制代码
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
        implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
  
    @Override
    public <T> T getBean(Class<T> requiredType) throws BeansException {
        return getBean(requiredType, (Object[]) null);
    }
  
    @Override
    public <T> T getBean(Class<T> requiredType, Object... args) throws BeansException {
        Assert.notNull(requiredType, "Required type must not be null");
        String[] beanNames = getBeanNamesForType(requiredType);
        if (beanNames.length > 1) {
            // 处理多个候选Bean的情况
            return resolveNamedBean(requiredType, beanNames, args);
        } else if (beanNames.length == 1) {
            return getBean(beanNames[0], requiredType, args);
        } else {
            throw new NoSuchBeanDefinitionException(requiredType, "expected at least 1 bean which qualifies as autowire candidate");
        }
    }
}
FactoryBean接口
java 复制代码
public interface FactoryBean<T> {
    T getObject() throws Exception;
    Class<?> getObjectType();
    boolean isSingleton();
}

具体使用场景

  1. 数据库连接工厂:不同类型的数据库连接
  2. 日志记录器工厂:不同级别的日志记录器
  3. 加密算法工厂:不同加密算法的实现
  4. UI组件工厂:不同平台的UI组件
  5. 数据解析器工厂:不同格式的数据解析器

抽象工厂模式

简介

抽象工厂模式(Abstract Factory Pattern)是一种创建型设计模式,它提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。抽象工厂模式是工厂方法模式的扩展。

核心流程

  1. 定义抽象产品族:声明多个相关的产品接口
  2. 定义具体产品族:实现每个产品接口
  3. 定义抽象工厂:声明创建产品族的方法
  4. 定义具体工厂:实现创建具体产品族的逻辑
  5. 客户端调用:通过工厂创建产品族
抽象工厂模式流程图

产品族关系 产品族层次 抽象工厂层次 产品族1: A1 + B1 产品族2: A2 + B2 具体产品A1 抽象产品A 具体产品A2 具体产品B1 抽象产品B 具体产品B2 具体工厂1 抽象工厂接口 具体工厂2 创建产品族1 创建产品族2 客户端 选择具体工厂 调用抽象工厂方法 工厂创建产品族 返回产品族对象 客户端使用产品族

实现示例

产品族接口
java 复制代码
// 抽象产品A
public interface ProductA {
    void operationA();
}

// 抽象产品B
public interface ProductB {
    void operationB();
}

// 具体产品A1
public class ConcreteProductA1 implements ProductA {
    @Override
    public void operationA() {
        System.out.println("产品A1的操作");
    }
}

// 具体产品A2
public class ConcreteProductA2 implements ProductA {
    @Override
    public void operationA() {
        System.out.println("产品A2的操作");
    }
}

// 具体产品B1
public class ConcreteProductB1 implements ProductB {
    @Override
    public void operationB() {
        System.out.println("产品B1的操作");
    }
}

// 具体产品B2
public class ConcreteProductB2 implements ProductB {
    @Override
    public void operationB() {
        System.out.println("产品B2的操作");
    }
}
抽象工厂和具体工厂
java 复制代码
// 抽象工厂
public interface AbstractFactory {
    ProductA createProductA();
    ProductB createProductB();
}

// 具体工厂1
public class ConcreteFactory1 implements AbstractFactory {
    @Override
    public ProductA createProductA() {
        return new ConcreteProductA1();
    }
  
    @Override
    public ProductB createProductB() {
        return new ConcreteProductB1();
    }
}

// 具体工厂2
public class ConcreteFactory2 implements AbstractFactory {
    @Override
    public ProductA createProductA() {
        return new ConcreteProductA2();
    }
  
    @Override
    public ProductB createProductB() {
        return new ConcreteProductB2();
    }
}
客户端使用
java 复制代码
public class Client {
    public static void main(String[] args) {
        AbstractFactory factory1 = new ConcreteFactory1();
        ProductA productA1 = factory1.createProductA();
        ProductB productB1 = factory1.createProductB();
    
        productA1.operationA();
        productB1.operationB();
    
        AbstractFactory factory2 = new ConcreteFactory2();
        ProductA productA2 = factory2.createProductA();
        ProductB productB2 = factory2.createProductB();
    
        productA2.operationA();
        productB2.operationB();
    }
}

重难点分析

重难点1:产品族的扩展
  • 问题:如何添加新的产品族
  • 解决方案:创建新的具体工厂类
重难点2:产品族的约束
  • 问题:如何保证产品族内产品的一致性
  • 解决方案:在抽象工厂中定义产品族的创建规则
重难点3:工厂选择策略
  • 问题:如何选择合适的工厂
  • 解决方案
    • 配置文件驱动
    • 环境变量控制
    • 策略模式结合

Spring中的源码分析

ApplicationContext作为抽象工厂
java 复制代码
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
        MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
  
    // 获取不同类型的Bean
    Object getBean(String name) throws BeansException;
    <T> T getBean(String name, Class<T> requiredType) throws BeansException;
    <T> T getBean(Class<T> requiredType) throws BeansException;
  
    // 获取Bean的提供者
    <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
    <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
}
ConfigurableApplicationContext
java 复制代码
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {
  
    // 配置Bean定义
    void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor);
  
    // 刷新上下文
    void refresh() throws BeansException, IllegalStateException;
  
    // 注册关闭钩子
    void registerShutdownHook();
}
AbstractApplicationContext的实现
java 复制代码
public abstract class AbstractApplicationContext extends DefaultResourceLoader
        implements ConfigurableApplicationContext {
  
    @Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // 准备刷新
            prepareRefresh();
        
            // 获取BeanFactory
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        
            // 准备BeanFactory
            prepareBeanFactory(beanFactory);
        
            try {
                // 后处理BeanFactory
                postProcessBeanFactory(beanFactory);
            
                // 调用BeanFactoryPostProcessor
                invokeBeanFactoryPostProcessors(beanFactory);
            
                // 注册BeanPostProcessor
                registerBeanPostProcessors(beanFactory);
            
                // 初始化MessageSource
                initMessageSource();
            
                // 初始化ApplicationEventMulticaster
                initApplicationEventMulticaster();
            
                // 刷新特定上下文
                onRefresh();
            
                // 注册监听器
                registerListeners();
            
                // 实例化所有非懒加载的单例Bean
                finishBeanFactoryInitialization(beanFactory);
            
                // 完成刷新
                finishRefresh();
            } catch (BeansException ex) {
                // 异常处理
                destroyBeans();
                cancelRefresh(ex);
                throw ex;
            } finally {
                resetCommonCaches();
            }
        }
    }
}

具体使用场景

  1. UI框架:不同操作系统的UI组件
  2. 数据库访问层:不同数据库的DAO实现
  3. 游戏引擎:不同平台的游戏资源
  4. 消息队列:不同消息中间件的实现
  5. 缓存系统:不同缓存技术的实现

三种模式对比总结

三种模式对比流程图

模式特点对比 抽象工厂模式 工厂方法模式 单例模式 是 否 单例: 1个实例 工厂方法: 1个产品 抽象工厂: 1个产品族 选择具体工厂 客户端 调用createProductA/B 工厂创建产品族 返回产品族对象 选择具体工厂 客户端 调用createProduct 工厂创建产品 返回具体产品 调用getInstance 客户端 实例存在? 返回现有实例 创建新实例 返回新实例

模式对比表格

特性 单例模式 工厂方法模式 抽象工厂模式
目的 确保只有一个实例 创建单一产品 创建产品族
复杂度 简单 中等 复杂
扩展性 难以扩展 易于扩展新产品 难以扩展新产品族
使用场景 配置管理、连接池 单一产品创建 相关产品族创建
Spring应用 Bean单例管理 FactoryBean ApplicationContext
设计原则 单一职责 开闭原则 开闭原则

选择指南

何时使用单例模式?
  • 需要全局唯一的实例
  • 资源消耗大的对象(如数据库连接池)
  • 配置管理器
  • 日志记录器
何时使用工厂方法模式?
  • 需要创建单一类型的产品
  • 产品类型可能变化
  • 需要解耦产品创建和使用
  • 符合开闭原则要求
何时使用抽象工厂模式?
  • 需要创建相关的产品族
  • 产品族内产品需要保持一致
  • 系统需要支持多个产品族
  • 产品族相对稳定

实际项目中的应用建议

  1. 简单配置管理 → 单例模式
  2. 数据访问层 → 工厂方法模式
  3. UI框架 → 抽象工厂模式
  4. 缓存系统 → 单例模式 + 工厂方法模式
  5. 插件系统 → 抽象工厂模式

面试高频点总结

面试知识点思维导图

设计模式面试点 单例模式 工厂方法模式 抽象工厂模式 设计原则 实际应用 实现方式 线程安全 序列化问题 反射攻击 Spring实现 饿汉式 懒汉式 双重检查锁定 静态内部类 枚举 synchronized volatile 指令重排序 readResolve 构造函数检查 BeanFactory ConcurrentHashMap 与简单工厂区别 解决的问题 Spring实现 开闭原则 扩展性 解耦 可维护性 FactoryBean getObject方法 与工厂方法区别 优缺点 模式选择 产品族 复杂度 产品一致性 扩展困难 简单创建 单一产品 产品族 单一职责 开闭原则 里氏替换 接口隔离 依赖倒置 Spring框架 MyBatis 数据库连接池 日志框架

单例模式面试点

1. 单例模式的实现方式有哪些?
  • 饿汉式(线程安全,但可能浪费内存)
  • 懒汉式(非线程安全)
  • 双重检查锁定(线程安全,性能较好)
  • 静态内部类(线程安全,延迟加载)
  • 枚举(线程安全,防止反射攻击)
2. 为什么双重检查锁定需要volatile关键字?
  • 防止指令重排序
  • 确保多线程环境下的可见性
  • 避免创建不完整的对象
3. 单例模式在Spring中是如何实现的?
  • 通过BeanFactory管理单例Bean
  • 使用ConcurrentHashMap缓存单例对象
  • 支持单例、原型、请求、会话等作用域

工厂方法模式面试点

1. 工厂方法模式与简单工厂模式的区别?
  • 简单工厂:一个工厂类创建所有产品
  • 工厂方法:每个产品对应一个工厂类
  • 工厂方法模式更符合开闭原则
2. 工厂方法模式解决了什么问题?
  • 解耦产品创建和使用
  • 支持开闭原则
  • 提高代码的可维护性
3. Spring中的FactoryBean是什么?
  • 特殊的Bean,用于创建其他Bean
  • 实现FactoryBean接口
  • 通过getObject()方法返回实际对象

抽象工厂模式面试点

1. 抽象工厂模式与工厂方法模式的区别?
  • 工厂方法:一个工厂创建一个产品
  • 抽象工厂:一个工厂创建一系列相关产品
  • 抽象工厂模式更复杂,但更灵活
2. 抽象工厂模式的优缺点?
  • 优点:支持产品族,保证产品一致性
  • 缺点:难以扩展新产品,增加系统复杂度
3. 如何选择合适的设计模式?
  • 简单创建:简单工厂
  • 单一产品:工厂方法
  • 产品族:抽象工厂
  • 复杂创建:建造者模式

综合面试点

1. 设计模式的原则?
  • 单一职责原则
  • 开闭原则
  • 里氏替换原则
  • 接口隔离原则
  • 依赖倒置原则
2. 创建型模式的特点?
  • 关注对象的创建过程
  • 隐藏创建细节
  • 提供灵活的创建方式
  • 支持开闭原则
3. 在项目中的实际应用?
  • Spring框架中的Bean管理
  • MyBatis中的SqlSessionFactory
  • 数据库连接池的实现
  • 日志框架的设计

总结

本文详细介绍了三种重要的创建型设计模式:

  1. 单例模式:确保类只有一个实例,适用于配置管理、连接池等场景
  2. 工厂方法模式:将产品创建延迟到子类,适用于需要扩展产品的场景
  3. 抽象工厂模式:创建产品族,适用于需要保证产品一致性的场景

每种模式都有其特定的使用场景和实现方式,在实际开发中需要根据具体需求选择合适的设计模式。Spring框架中大量使用了这些设计模式,理解这些模式有助于更好地使用和理解Spring框架。

在面试中,除了要掌握这些模式的基本概念和实现方式外,还要能够结合实际项目经验,说明在什么情况下使用哪种模式,以及如何解决实际开发中遇到的问题。

相关推荐
bkspiderx6 小时前
C++设计模式之创建型模式:原型模式(Prototype)
c++·设计模式·原型模式
phdsky11 小时前
【设计模式】中介者模式
c++·设计模式·中介者模式
软件黑马王子14 小时前
C#练习题——泛型实现单例模式和增删改查
开发语言·单例模式·c#
phdsky14 小时前
【设计模式】状态模式
设计模式·状态模式
bkspiderx14 小时前
C++设计模式之创建型模式:抽象工厂模式(Abstract Factory)
c++·设计模式·抽象工厂模式
Chan1615 小时前
【 设计模式 | 创建型模式 建造者模式 】
java·spring boot·设计模式·java-ee·intellij-idea·建造者模式
澄澈i15 小时前
设计模式学习[19]---单例模式(饿汉式/懒汉式)
学习·单例模式·设计模式
bkspiderx16 小时前
C++设计模式之创建型模式:工厂方法模式(Factory Method)
c++·设计模式·工厂方法模式
yujkss16 小时前
23种设计模式之【工厂方法模式】-核心原理与 Java实践
java·设计模式·工厂方法模式