← 返回题目列表

抽象工厂如何支持运行时切换产品族?

高频 中等 第 8 / 25 题 更新于 2026/08/01
抽象工厂模式运行时切换配置切换产品族

简化版

抽象工厂可以把产品族选择集中到配置、环境变量、租户信息或策略路由中,运行时根据条件选择具体工厂,再由该工厂创建整套产品。关键是切换边界要清楚:同一次请求或同一个租户内应使用同一个工厂,避免一半产品来自旧族、一半来自新族。

详细版

运行时切换常见于多云适配、多数据库方言、跨平台 UI、灰度环境和多租户系统。做法通常是维护一个 FactoryProvider 或注册表,根据上下文选择具体抽象工厂:

CloudFactory factory = factoryProvider.getFactory(tenantId);
StorageClient storage = factory.createStorageClient();
QueueClient queue = factory.createQueueClient();

切换时要注意:

  1. 工厂选择应在一个明确边界内完成,例如请求开始、任务开始或租户初始化;
  2. 同一业务流程内不要多次读取可变配置导致工厂不一致;
  3. 工厂本身若持有资源,要处理缓存、刷新和关闭;
  4. 灰度切换要有回滚和观测指标。

运行时切换不是把 if-else 到处散开,而是把选择点集中起来,让业务代码仍依赖抽象工厂和抽象产品。

完整版教学

一、运行时切换解决什么问题

抽象工厂的基本版本常在启动时确定一个产品族,比如当前平台是 Windows,就使用 WindowsUiFactory。但真实项目里,产品族可能需要按租户、区域、灰度策略或请求参数动态选择。

例如同一个 SaaS 系统里:

租户 A -> AWS 产品族
租户 B -> 阿里云产品族
租户 C -> 本地私有云产品族

如果选择逻辑散落在每个业务类中,就会出现存储走 AWS、队列走阿里云的混搭风险。运行时抽象工厂的目标是集中选择,并让后续产品都来自同一个具体工厂。

二、FactoryProvider 是常见落地方式

可以增加一个工厂提供者,用它根据上下文返回具体工厂。业务流程只在入口处选择一次。

public class CloudFactoryProvider {
    private final Map<String, CloudFactory> factories;

    public CloudFactory getFactory(String vendor) {
        CloudFactory factory = factories.get(vendor);
        if (factory == null) {
            throw new IllegalArgumentException("unknown vendor: " + vendor);
        }
        return factory;
    }
}

请求处理时:

CloudFactory factory = provider.getFactory(request.vendor());
StorageClient storage = factory.createStorageClient();
QueueClient queue = factory.createQueueClient();
MonitorClient monitor = factory.createMonitorClient();

这个写法把运行时条件限制在工厂选择处,而不是让三个产品各自读取配置。

三、同一次流程内为什么要固定工厂

如果业务流程中每一步都重新读取配置,切换窗口就可能产生不一致。比如 T1 时读到 aws 创建了 storage,T2 时配置变成 aliyun 创建了 queue,最后同一订单同时使用两个云厂商。

T=0  请求开始,配置 vendor=aws
T=1  创建 AwsStorageClient
T=2  配置灰度切到 aliyun
T=3  创建 AliyunQueueClient
T=4  同一流程产品族混搭

更稳的做法是在请求开始时解析出 CloudFactory,并把它作为上下文传递下去。同一流程内不再重新选择工厂,避免半路切换造成组合不一致。

记忆钩子:运行时可以切族,但一次业务流程里不要边跑边换族。

四、多租户场景要注意缓存 key

多租户切换常需要缓存具体工厂或产品对象。缓存 key 必须包含影响创建结果的维度,例如租户、厂商、region、凭证版本。key 少一维就可能串租户。

record FactoryKey(String tenantId, String vendor, String region, String credentialVersion) {}

假设有 100 个租户,每个租户 2 个 region,如果只按 vendor 缓存,就会把不同租户的凭证混用。正确缓存规模可能是 100 * 2 = 200 个工厂或配置对象,而不是 2 个厂商对象。

当然,缓存规模也要控制。工厂如果持有连接池和线程池,200 个工厂可能带来明显资源压力,需要惰性创建、淘汰、共享底层连接或交给容器管理。

五、灰度切换要考虑回滚和观测

运行时切换常用于灰度发布,比如 5% 租户从旧云厂商切到新云厂商。抽象工厂可以把灰度选择集中在 provider 中,但还需要指标验证。

建议观测:

指标作用
每个产品族请求量确认流量比例正确
创建失败率发现新工厂初始化问题
产品调用错误码比较新旧产品族质量
回滚耗时验证配置切回是否生效

如果灰度期间发现新产品族失败率从 0.1% 升到 3%,provider 应能快速切回旧工厂。抽象工厂解决结构切换,灰度系统解决风险控制。

六、和策略模式有什么区别

运行时选择具体工厂,看起来像策略模式。区别在于:策略模式通常选择一个算法行为,抽象工厂选择一组相关产品。一个租户选择 AwsFactory 后,会得到 AWS 的 storage、queue、monitor 一整套产品。

维度策略模式运行时抽象工厂
选择对象一个算法/行为一组产品创建者
一致性重点行为可替换产品族配套
典型例子折扣算法多云客户端族
切换边界单次算法调用请求、租户、环境

如果只切换一个算法,用策略就够;如果切换后要创建整套兼容对象,抽象工厂更合适。

七、常见误区与追问

  • 误区:运行时切换就是到处写 if-else。 正确做法是集中选择具体工厂,业务代码仍依赖抽象。
  • 误区:每次创建产品都重新读配置更灵活。 同一流程内多次读取可变配置会造成产品族混搭。
  • 误区:工厂缓存只按 vendor 就够。 多租户、多 region、多凭证版本都可能影响创建结果。
  • 误区:抽象工厂能自动完成灰度安全。 它只提供结构,灰度比例、指标和回滚仍要单独设计。
  • 追问:工厂选择应该放在哪里? 放在请求入口、任务入口、租户上下文初始化等明确边界。
  • 追问:如何避免切换期间旧资源泄漏? 对被替换工厂做引用计数、延迟关闭或容器生命周期管理。
  • 追问:运行时切换和策略模式怎么区分? 策略切一个行为,抽象工厂切一整套产品族。

八、加强记忆

运行时抽象工厂的核心是“集中选族,流程内固定”。按配置、租户或灰度选择具体工厂后,一整套产品都从这个工厂创建;同时要处理缓存 key、资源释放、观测回滚和与策略模式的边界。