设计模式详解:单例模式、工厂方法模式、抽象工厂模式
目录
单例模式
简介
单例模式(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 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
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 {
@Override
public void use() {
System.out.println("使用产品A");
}
}
// 具体产品B
public class ConcreteProductB implements Product {
@Override
public void use() {
System.out.println("使用产品B");
}
}
工厂接口和实现
// 抽象工厂
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();
}
}
客户端使用
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 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接口
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 {
@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的操作");
}
}
抽象工厂和具体工厂
// 抽象工厂
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();
}
}
客户端使用
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 {
// 获取不同类型的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
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 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();
}
}
}
}
具体使用场景
- 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框架。
在面试中,除了要掌握这些模式的基本概念和实现方式外,还要能够结合实际项目经验,说明在什么情况下使用哪种模式,以及如何解决实际开发中遇到的问题。
415

被折叠的 条评论
为什么被折叠?



