【设计模式】什么场景可以考虑使用简单工厂模式

1.概述

工厂模式是一种创建型模式,主要作用就是创建对象,将对象的创建过程和使用的过程进行解耦。我们平时说的工厂模式实际上是对三种不同类型的工厂模式的统称,简单工厂、工厂方法、抽象工厂 ,而在23种设计模式中,只定义了工厂方法和抽象工厂,将简单工厂看作是工厂方法的一种特例,本篇主要讲述的是简单工厂

简单工厂,就像它的名字一样突出一个简单 ,就是将业务流程代码中直接使用new关键字来创建对象,修改为通过一个工厂类创建对象,这就是简单工厂。如果仅仅只是将new操作转移到了一个新的类里面,看起来只是在徒增类的数量和代码量,并没有什么意义。

那为什么我们还要使用简单工厂模式呢?

是因为它在某些特定的场景下有其存在的价值,让我们从不同的场景来看看。

2.从不同场景看简单工厂的意义

2.1.框架或工具的封装

除了日常业务开发之外,有时候我们也可能得做一些框架或者工具的开发,这部分开发出来的工具需要提供给其他的开发人员使用,而他们在使用的时候,第一步就是获取到这个工具的对象 ,这种情况下就可以给使用者提供一个简单工厂 ,让使用者通过工厂来创建对象。

例如在Java中使用日历相关的工具Calendar就是通过简单工厂提供的获取日历对象的方法,如下图:

我们做类似的框架或工具开发的时候,完全可以参照Calendar的方式,给使用者提供创建对象的能力,从而让使用者无需关心对象的实际创建过程,而是只需要通过特定的方法和参数,就能获取到一个可供使用的对象。

2.2.复杂业务对象的创建

即使是在做业务相关开发,有时候也会涉及到一些相对复杂的业务对象的创建(例如DDD中的领域对象),这时候可以使用简单工厂将产品对象 的创建流程从业务流程中抽离。由于创建对象的复杂性被隔离在工厂类中,因此当涉及到产品类的变化时,比如增加新功能、改变实现方式等,只需要改动工厂类,不会对使用产品的其他模块造成影响,有利于产品创建逻辑的集中管理,以及系统的维护和版本升级。


之前在做一个项目开发的时候使用过简单工厂处理过Domain对象的创建,先说一下背景:

这个项目使用的时COLA架构(一种DDD的代码层面实践的框架),在这个架构的分层中Domain层属于最底层,如下图中的demoWeb-domain

Domain层中会完成大部分的业务逻辑,但我们知道业务流程中往往伴随着与中间件其他服务之间的交互(例如数据库的存取操作),但这部分交互不应该由领域对象来实现。

COLA中的做法就是提供一个"防腐层"来实现,所谓的防腐层就是在domain层中定义与中间件交互的interface再交由infrastructure来实现,这样在domain层就只需要关心自己需要做一个什么交互 ,而不需要关心具体的交互实现,如下的红框所示。

背景介绍完了,说一下这里面存在一个问题,就是Domain对象为了保证业务模型的纯洁性,一般不会使用Spring来管理领域对象的生命周期,在这种情况下如何才能将gatewayImpl对象注入到领域对象中呢?

答案是在Infrastructure层中,将getewayImpl对象set到领取对象中,如果领域对象的创建过程相对复杂,就可以使用简单工厂进行创建,统一管理创建逻辑,代码如下:

java 复制代码
@Component
public class MsgWecomAppFactory {

    @Resource
    private MsgWecomGateway msgWecomGateway;
    @Resource
    private MsgWecomCacheGateway msgWecomCacheGateway;

    @Resource
    private WecomHttpHelper wecomHttpHelper;

    /**
     * 创建企业微信应用号领域对象,用于确认消息
     *
     * @param agentId 应用号ID
     * @return 领域对象
     */
    public MsgWecomApp createMsgWecomAppForConfirm(String agentId) {
        MsgWecomApp msgWecomApp = new MsgWecomApp();
        // 1.查询企业微信应用配置信息
        Optional<MsgWecomAppConfig> appConfig = msgWecomGateway.getWecomAppConfigByAgentId(agentId);
        Assert.isTrue(appConfig.isPresent(), "企业微信应用号配置信息不存在,执行失败,agentId:" + agentId);
        msgWecomApp.setConfig(appConfig.get());

        msgWecomApp.setMsgWecomCacheGateway(msgWecomCacheGateway);
        msgWecomApp.setMsgWecomGateway(msgWecomGateway);
        msgWecomApp.setWecomHttpHelper(wecomHttpHelper);
        return msgWecomApp;
    }

    /**
     * 创建企业微信应用号领域对象,用于发送消息
     *
     * @param agentId 应用号ID
     * @param phones  手机号
     * @return 领域对象
     */
    public MsgWecomApp createMsgWecomAppForSend(String agentId, Set<String> phones) {
        MsgWecomApp msgWecomApp = new MsgWecomApp();
        // 1.查询企业微信应用配置信息
        Optional<MsgWecomAppConfig> appConfig = msgWecomGateway.getWecomAppConfigByAgentId(agentId);
        Assert.isTrue(appConfig.isPresent(), "企业微信应用号配置信息不存在");
        msgWecomApp.setConfig(appConfig.get());

        // 2.查询用户信息,优先使用手机号查询,如果手机号为空或手机号未查询到则使用邮箱查询
        List<MsgWecomMemberInfo> msgWecomMemberInfos = new ArrayList<>();
        if (CollUtil.isNotEmpty(phones)) {
            msgWecomMemberInfos = msgWecomGateway.listWecomMemberInfoByPhone(phones);
            // 对比参数中的手机号与查询到的手机号,获取差集
            List<String> existPhone = msgWecomMemberInfos.stream().map(MsgWecomMemberInfo::getPhone).collect(Collectors.toList());
            msgWecomApp.setNotExistPhones(CollUtil.subtractToList(phones, existPhone));
        }

        msgWecomApp.setToSendMemberInfos(msgWecomMemberInfos);

        msgWecomApp.setMsgWecomCacheGateway(msgWecomCacheGateway);
        msgWecomApp.setMsgWecomGateway(msgWecomGateway);
        msgWecomApp.setWecomHttpHelper(wecomHttpHelper);
        return msgWecomApp;
    }
}

2.3.与其他模式的组合使用

策略模式中的选择器 实现:

SpringBoot优雅使用策略模式这一篇博客中提到了如何使用Spring对Bean的管理能力,来实现策略模式的选择器。同样的,如果没有使用Spring或者业务对象的生命周期不需要Spring框架介入时,就可以使用简单工厂+单例的方式来实现,代码如下:

java 复制代码
/**
 * 策略选择器工厂
 */
public class StrategySelectorFactory {

    private static final Map<String, Strategy> STRATEGY_MAP = new java.util.HashMap<>();

    static {
        STRATEGY_MAP.put("A", new StrategyA());
        STRATEGY_MAP.put("B", new StrategyB());
    }

    public static Strategy getStrategy(String strategyKey) {
        if (strategyKey == null || strategyKey.isEmpty()) {
            throw new IllegalArgumentException("strategyKey can not be empty");
        }
        return STRATEGY_MAP.get(strategyKey);
    }
}

3.总结

本篇主要讲述了工厂模式中的特例:简单工厂模式,并通过3种不同的场景来介绍这种模式存在的意义,有以下几方面:

  • 封装对象创建过程: 将对象的创建过程封装工厂类中,使用者无需了解具体产品的创建细节,只需调用工厂类提供的静态方法即可得到所需的产品对象。这样可以隐藏产品类的具体实现,降低耦合度。
  • 控制逻辑集中: 将复杂对象的创建对象逻辑集中在工厂类中,如果需要修改或扩展产品类型时,只需要修改工厂类中的代码。这使得添加新产品或者调整产品创建逻辑更加方便、集中管理,也有利于系统的维护和版本升级
  • 组合其他模式满足特定的需求 : 在某些应用场景中,如根据参数动态选择不同类型的对象实例化,简单工厂+单例提供简洁有效的解决方案,避免了直接使用 new 关键字创建对象带来的硬编码问题。

最后,虽然简单工厂模式在一定程度上提高了灵活性和可维护性,但它也有其局限性,例如违反了开闭原则,每增加一个新产品就需要修改工厂类的代码。

但瑕不掩瑜,简单工厂模式在很多简单场景下发挥着重要的作用。

相关推荐
BillKu27 分钟前
Java + Spring Boot + Mybatis 插入数据后,获取自增 id 的方法
java·tomcat·mybatis
全栈凯哥28 分钟前
Java详解LeetCode 热题 100(26):LeetCode 142. 环形链表 II(Linked List Cycle II)详解
java·算法·leetcode·链表
chxii29 分钟前
12.7Swing控件6 JList
java
全栈凯哥30 分钟前
Java详解LeetCode 热题 100(27):LeetCode 21. 合并两个有序链表(Merge Two Sorted Lists)详解
java·算法·leetcode·链表
YuTaoShao31 分钟前
Java八股文——集合「List篇」
java·开发语言·list
PypYCCcccCc36 分钟前
支付系统架构图
java·网络·金融·系统架构
华科云商xiao徐1 小时前
Java HttpClient实现简单网络爬虫
java·爬虫
扎瓦1 小时前
ThreadLocal 线程变量
java·后端
BillKu1 小时前
Java后端检查空条件查询
java·开发语言
jackson凌2 小时前
【Java学习笔记】String类(重点)
java·笔记·学习