一文穿透抽象工厂模式

一、什么是抽象工厂模式

为创建一组相关或相互依赖的对象提供一个接口,而无需指定它们的具体类。

二、为什么要使用抽象工厂模式

1、对于不同产品系有较多共性时,可以使用抽象工厂模式,提高了组件的复用性和扩展性。比如JDBC实现对不同数据库进行增删改查创建的抽象工厂模式。
2、解决跨平台引起的兼容性问题。比如华为公司和苹果公司都生产电脑、手机和手表,具有不同的厂家去具体生产两家公司的产品。

三、UML图

比如生产华为和苹果产品的UML图如下所示
在这里插入图片描述
在这个UML图中,可看出抽象工厂模式包含四个角色,分别为:

  • 抽象工厂:Factory;
  • 抽象产品:Computer,Phone,Watch;
  • 具体工厂:HuaWeiFactory,AppleFactory;
  • 具体产品:HuaWeiComputer,HuaWeiPhone,HuaWeiWatch,AppleComputer,ApplePhone,AppleWatch。

四、代码实现

1、抽象的生产工厂
public interface Factory {

    /**
     * 生产电脑
     * @return {@link Computer}
     */
    Computer produceComputer();

    /**
     * 生产手机
     * @return {@link Phone}
     */
    Phone producePhone();

    /**
     * 生产手表
     * @return {@link Watch}
     */
    Watch produceWatch();

}
2、华为的生产工厂
public class HuaWeiFactory implements Factory {

    @Override
    public Computer produceComputer() {
        return new HuaWeiComputer();
    }

    @Override
    public Phone producePhone() {
        return new HuaWeiPhone();
    }

    @Override
    public Watch produceWatch() {
        return new HuaWeiWatch();
    }
}
3、苹果的生产工厂
public class AppleFactory implements Factory {

    @Override
    public Computer produceComputer() {
        return new AppleComputer();
    }

    @Override
    public Phone producePhone() {
        return new ApplePhone();
    }

    @Override
    public Watch produceWatch() {
        return new AppleWatch();
    }
}

在此省略了抽象产品和具体产品类,关键理解这个模式,代码都是很简单的。

4、客户端调用
public class ClientTest {

    @Test
    public void testFactory() {
        // 华为工厂生产的电脑、手机和手表
        Factory huaWeiFactory = new HuaWeiFactory();
        huaWeiFactory.produceComputer();
        huaWeiFactory.producePhone();
        huaWeiFactory.produceWatch();

        // 苹果工厂生产的电脑、手机和手表
        Factory appleFactory = new AppleFactory();
        appleFactory.produceComputer();
        appleFactory.producePhone();
        appleFactory.produceWatch();
    }

}

五、优点

1、可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
2、当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品组,满足单一职责原则。
3、抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改原代码,符合开闭原则。

六、缺点

1、代码量增加了。
2、学习成本较大,需具有系统抽象性思维和一定的理解能力。
3、产品结构不能轻易改变。意思即增加了或移除了某个产品,相应的抽象工厂和具体工厂都得调整。

七、应用场景

1、当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、洗衣机、空调等。
2、系统中有多个产品族,但每次只使用其中的某一族产品。如有人只喜欢穿某一个品牌的衣服和鞋。
3、系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。

八、源码解析

1、JDK

JDK中java.sql包运用了抽象工厂模式,其UML图如下所示:
在这里插入图片描述
如果对JDBC不了解,请看《从JDBC到hibernate再到mybatis之路》一文了解。

2、Spring

Spring框架中的核心之一为IoC,其底层的BeanFactory运用了抽象工厂模式,源码【版本:spring-beans:5.1.19.REALEASE,不同版本略有差异,但大同小异】如下:

package org.springframework.beans.factory;

import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;

/**
 * 访问Spring bean容器的根接口
 * 此接口由包含多个bean定义的对象实现,每个bean定义由String名称唯一标识。根据bean定义,工厂将返回包含对象的独立实例(Prototype设计模式)或单个共享实例(Singleton设计模式的高级替代,其中实例是工厂范围内的Singleton)。将返回哪种类型的实例取决于bean工厂配置:API是相同的。自Spring 2.0以来,根据具体的应用程序上下文(例如,web环境中的“请求”和“会话”作用域),可以使用更多的作用域。
 * Spring的依赖注入功能是使用此BeanFactory接口及其子接口实现的。
 * Bean工厂实现应该尽可能支持标准Bean生命周期接口。整套初始化方法及其标准顺序如下:
 * <ol>
 * <li>BeanNameAware's {@code setBeanName}
 * <li>BeanClassLoaderAware's {@code setBeanClassLoader}
 * <li>BeanFactoryAware's {@code setBeanFactory}
 * <li>EnvironmentAware's {@code setEnvironment}
 * <li>EmbeddedValueResolverAware's {@code setEmbeddedValueResolver}
 * <li>ResourceLoaderAware's {@code setResourceLoader}
 * (only applicable when running in an application context)
 * <li>ApplicationEventPublisherAware's {@code setApplicationEventPublisher}
 * (only applicable when running in an application context)
 * <li>MessageSourceAware's {@code setMessageSource}
 * (only applicable when running in an application context)
 * <li>ApplicationContextAware's {@code setApplicationContext}
 * (only applicable when running in an application context)
 * <li>ServletContextAware's {@code setServletContext}
 * (only applicable when running in a web application context)
 * <li>{@code postProcessBeforeInitialization} methods of BeanPostProcessors
 * <li>InitializingBean's {@code afterPropertiesSet}
 * <li>a custom init-method definition
 * <li>{@code postProcessAfterInitialization} methods of BeanPostProcessors
 * </ol>
 *
 * 在关闭bean工厂时,以下生命周期方法适用:
 * <ol>
 * <li>{@code postProcessBeforeDestruction} methods of DestructionAwareBeanPostProcessors
 * <li>DisposableBean's {@code destroy}
 * <li>a custom destroy-method definition
 * </ol>
 *
 */
public interface BeanFactory {

	// 工厂bean前缀,用于区分bean工厂
	String FACTORY_BEAN_PREFIX = "&";

	// 根据名称返回bean
	Object getBean(String name) throws BeansException;

	// 根据名称获取bean, 并转化为指定的对象
	<T> T getBean(String name, Class<T> requiredType) throws BeansException;

	// 根据名称获取bean, 并传入构造该bean的参数
	Object getBean(String name, Object... args) throws BeansException;

	// 根据字节码获取bean
	<T> T getBean(Class<T> requiredType) throws BeansException;

	// 根据字节码获取bean,并传入构造该bean的参数
	<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

	// 根据字节码获取指定bean的提供者
	<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);

	// 根据指定的解析类型获取bean的提供者
	<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);

	// 根据名称判断工厂是否包含同样的bean
	boolean containsBean(String name);

	// 根据名称判断bean是否为单例对象
	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

	// 根据名称判断bean是否为原型对象
	boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

	// 根据名称判断bean是否能被指定的可解析类型解析
	boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

	// 根据名称判断bean是否被指定的字节码对应的类进行解析
	boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

	// 根据名称获取bean的类型对应的字节码
	@Nullable
	Class<?> getType(String name) throws NoSuchBeanDefinitionException;

	// 获取bean的别名
	String[] getAliases(String name);

}

查看BeanFactory的实现类多达50个,如下图所示:
在这里插入图片描述
其中有接口、抽象类和具体的实现类,可以根据不同的工厂创建不同的bean对象。
更具体的可自行查看源码,本文主要介绍抽象工厂模式,因此不做展开,后面会在Spring专栏中全面解析。

九、注意点

在java面向对象开发中最难的是抽象,因此要想运用好抽象工厂模式,要有一定的抽象思维,能够找到抽象产品,才能将抽象工厂模式的能力发挥出来。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值