设计模式详解:单例模式、工厂方法模式、抽象工厂模式
设计模式详解:单例模式、工厂方法模式、抽象工厂模式
目录
- 单例模式
- 工厂方法模式
- 抽象工厂模式
- 三种模式对比总结
- 面试高频点总结
单例模式
简介
单例模式(Singleton Pattern)是一种创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点。单例模式的核心思想是控制实例的创建过程,保证系统中只有一个实例存在。
核心流程
- 私有化构造函数:防止外部直接创建实例
- 提供静态方法:作为全局访问点
- 延迟初始化:在需要时才创建实例
- 线程安全:确保多线程环境下的安全性
单例模式流程图
实现方式
单例模式实现方式对比流程图
1. 饿汉式(线程安全)
public class EagerSingleton {private static final EagerSingleton instance = new EagerSingleton();private EagerSingleton() {}public static EagerSingleton getInstance() {return instance;}
}
2. 懒汉式(非线程安全)
public class LazySingleton {private static LazySingleton instance;private LazySingleton() {}public static LazySingleton getInstance() {if (instance == null) {instance = new LazySingleton();}return instance;}
}
3. 双重检查锁定(线程安全)
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. 枚举单例(推荐)
public enum EnumSingleton {INSTANCE;public void doSomething() {// 业务逻辑}
}
重难点分析
重难点1:线程安全问题
- 问题:多线程环境下可能创建多个实例
- 解决方案:
- 使用synchronized关键字
- 使用volatile关键字防止指令重排序
- 使用枚举实现(JVM保证线程安全)
重难点2:序列化问题
- 问题:反序列化时可能创建新实例
- 解决方案:实现readResolve()方法
private Object readResolve() {return instance;
}
重难点3:反射攻击
- 问题:通过反射可以绕过私有构造函数
- 解决方案:在构造函数中检查实例是否已存在
private LazySingleton() {if (instance != null) {throw new RuntimeException("单例模式被破坏");}
}
Spring中的源码分析
ApplicationContext的单例实现
// AbstractApplicationContext.java
public abstract class AbstractApplicationContext extends DefaultResourceLoaderimplements ConfigurableApplicationContext {// 单例Bean的缓存private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);@Overridepublic 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
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;}
}
具体使用场景
- 配置管理器:全局配置信息
- 数据库连接池:避免重复创建连接
- 日志记录器:统一日志输出
- 缓存管理器:全局缓存控制
- 线程池:资源池管理
工厂方法模式
简介
工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它定义了一个创建对象的接口,但让子类决定实例化哪一个类。工厂方法让类的实例化推迟到子类中进行。
核心流程
- 定义抽象产品接口:声明产品的通用方法
- 定义具体产品类:实现产品接口
- 定义抽象工厂接口:声明创建产品的方法
- 定义具体工厂类:实现创建具体产品的逻辑
- 客户端调用:通过工厂创建产品
工厂方法模式流程图
实现示例
产品接口和实现
// 抽象产品
public interface Product {void use();
}// 具体产品A
public class ConcreteProductA implements Product {@Overridepublic void use() {System.out.println("使用产品A");}
}// 具体产品B
public class ConcreteProductB implements Product {@Overridepublic void use() {System.out.println("使用产品B");}
}
工厂接口和实现
// 抽象工厂
public interface ProductFactory {Product createProduct();
}// 具体工厂A
public class ConcreteProductAFactory implements ProductFactory {@Overridepublic Product createProduct() {return new ConcreteProductA();}
}// 具体工厂B
public class ConcreteProductBFactory implements ProductFactory {@Overridepublic Product createProduct() {return new ConcreteProductB();}
}
客户端使用
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接口
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
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactoryimplements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {@Overridepublic <T> T getBean(Class<T> requiredType) throws BeansException {return getBean(requiredType, (Object[]) null);}@Overridepublic <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接口
public interface FactoryBean<T> {T getObject() throws Exception;Class<?> getObjectType();boolean isSingleton();
}
具体使用场景
- 数据库连接工厂:不同类型的数据库连接
- 日志记录器工厂:不同级别的日志记录器
- 加密算法工厂:不同加密算法的实现
- UI组件工厂:不同平台的UI组件
- 数据解析器工厂:不同格式的数据解析器
抽象工厂模式
简介
抽象工厂模式(Abstract Factory Pattern)是一种创建型设计模式,它提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。抽象工厂模式是工厂方法模式的扩展。
核心流程
- 定义抽象产品族:声明多个相关的产品接口
- 定义具体产品族:实现每个产品接口
- 定义抽象工厂:声明创建产品族的方法
- 定义具体工厂:实现创建具体产品族的逻辑
- 客户端调用:通过工厂创建产品族
抽象工厂模式流程图
实现示例
产品族接口
// 抽象产品A
public interface ProductA {void operationA();
}// 抽象产品B
public interface ProductB {void operationB();
}// 具体产品A1
public class ConcreteProductA1 implements ProductA {@Overridepublic void operationA() {System.out.println("产品A1的操作");}
}// 具体产品A2
public class ConcreteProductA2 implements ProductA {@Overridepublic void operationA() {System.out.println("产品A2的操作");}
}// 具体产品B1
public class ConcreteProductB1 implements ProductB {@Overridepublic void operationB() {System.out.println("产品B1的操作");}
}// 具体产品B2
public class ConcreteProductB2 implements ProductB {@Overridepublic void operationB() {System.out.println("产品B2的操作");}
}
抽象工厂和具体工厂
// 抽象工厂
public interface AbstractFactory {ProductA createProductA();ProductB createProductB();
}// 具体工厂1
public class ConcreteFactory1 implements AbstractFactory {@Overridepublic ProductA createProductA() {return new ConcreteProductA1();}@Overridepublic ProductB createProductB() {return new ConcreteProductB1();}
}// 具体工厂2
public class ConcreteFactory2 implements AbstractFactory {@Overridepublic ProductA createProductA() {return new ConcreteProductA2();}@Overridepublic ProductB createProductB() {return new ConcreteProductB2();}
}
客户端使用
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作为抽象工厂
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,MessageSource, ApplicationEventPublisher, ResourcePatternResolver {// 获取不同类型的BeanObject 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
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {// 配置Bean定义void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor);// 刷新上下文void refresh() throws BeansException, IllegalStateException;// 注册关闭钩子void registerShutdownHook();
}
AbstractApplicationContext的实现
public abstract class AbstractApplicationContext extends DefaultResourceLoaderimplements ConfigurableApplicationContext {@Overridepublic void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// 准备刷新prepareRefresh();// 获取BeanFactoryConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// 准备BeanFactoryprepareBeanFactory(beanFactory);try {// 后处理BeanFactorypostProcessBeanFactory(beanFactory);// 调用BeanFactoryPostProcessorinvokeBeanFactoryPostProcessors(beanFactory);// 注册BeanPostProcessorregisterBeanPostProcessors(beanFactory);// 初始化MessageSourceinitMessageSource();// 初始化ApplicationEventMulticasterinitApplicationEventMulticaster();// 刷新特定上下文onRefresh();// 注册监听器registerListeners();// 实例化所有非懒加载的单例BeanfinishBeanFactoryInitialization(beanFactory);// 完成刷新finishRefresh();} catch (BeansException ex) {// 异常处理destroyBeans();cancelRefresh(ex);throw ex;} finally {resetCommonCaches();}}}
}
具体使用场景
- UI框架:不同操作系统的UI组件
- 数据库访问层:不同数据库的DAO实现
- 游戏引擎:不同平台的游戏资源
- 消息队列:不同消息中间件的实现
- 缓存系统:不同缓存技术的实现
三种模式对比总结
三种模式对比流程图
模式对比表格
特性 | 单例模式 | 工厂方法模式 | 抽象工厂模式 |
---|---|---|---|
目的 | 确保只有一个实例 | 创建单一产品 | 创建产品族 |
复杂度 | 简单 | 中等 | 复杂 |
扩展性 | 难以扩展 | 易于扩展新产品 | 难以扩展新产品族 |
使用场景 | 配置管理、连接池 | 单一产品创建 | 相关产品族创建 |
Spring应用 | Bean单例管理 | FactoryBean | ApplicationContext |
设计原则 | 单一职责 | 开闭原则 | 开闭原则 |
选择指南
何时使用单例模式?
- 需要全局唯一的实例
- 资源消耗大的对象(如数据库连接池)
- 配置管理器
- 日志记录器
何时使用工厂方法模式?
- 需要创建单一类型的产品
- 产品类型可能变化
- 需要解耦产品创建和使用
- 符合开闭原则要求
何时使用抽象工厂模式?
- 需要创建相关的产品族
- 产品族内产品需要保持一致
- 系统需要支持多个产品族
- 产品族相对稳定
实际项目中的应用建议
- 简单配置管理 → 单例模式
- 数据访问层 → 工厂方法模式
- UI框架 → 抽象工厂模式
- 缓存系统 → 单例模式 + 工厂方法模式
- 插件系统 → 抽象工厂模式
面试高频点总结
面试知识点思维导图
单例模式面试点
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
- 数据库连接池的实现
- 日志框架的设计
总结
本文详细介绍了三种重要的创建型设计模式:
- 单例模式:确保类只有一个实例,适用于配置管理、连接池等场景
- 工厂方法模式:将产品创建延迟到子类,适用于需要扩展产品的场景
- 抽象工厂模式:创建产品族,适用于需要保证产品一致性的场景
每种模式都有其特定的使用场景和实现方式,在实际开发中需要根据具体需求选择合适的设计模式。Spring框架中大量使用了这些设计模式,理解这些模式有助于更好地使用和理解Spring框架。
在面试中,除了要掌握这些模式的基本概念和实现方式外,还要能够结合实际项目经验,说明在什么情况下使用哪种模式,以及如何解决实际开发中遇到的问题。