← 返回题目列表

抽象工厂在云厂商或数据库适配中怎么用?

高频 中等 第 9 / 25 题 更新于 2026/07/28
抽象工厂模式云厂商适配数据库方言架构设计

简化版

当系统要在不同云厂商或数据库之间切换,并且每个厂商都有一组相关客户端或方言对象时,可以用抽象工厂统一创建这一整套对象。这样业务代码依赖稳定接口,具体厂商差异集中在具体工厂和具体产品里。

详细版

以云厂商为例,抽象产品可以是:

interface StorageClient {
    void upload(String key, byte[] data);
}

interface QueueClient {
    void send(String topic, String message);
}

抽象工厂负责创建同一厂商的一组客户端:

interface CloudFactory {
    StorageClient createStorageClient();
    QueueClient createQueueClient();
}

阿里云工厂创建 AliStorageClient 和 AliQueueClient,AWS 工厂创建 AwsStorageClient 和 AwsQueueClient。业务层只使用 StorageClientQueueClient,不直接依赖厂商 SDK。

数据库适配也类似。可以把 SQL 构造器、类型映射器、分页方言封装成一个产品族,由 MySQLFactory、PostgreSQLFactory 分别创建。

完整版教学

一、这类场景为什么适合抽象工厂

云厂商和数据库适配的特点是:不是一个对象有差异,而是一整组对象都有差异。存储、队列、短信、监控客户端往往要来自同一个厂商;SQL 构造器、类型映射、分页语法也要来自同一种数据库方言。

如果分别选择每个对象,就容易混搭,导致运行时错误或配置不一致。

二、用产品接口隔离第三方 SDK

业务层最好不要直接使用第三方 SDK 类型,而是定义自己的稳定接口:

interface ObjectStorage {
    String putObject(String bucket, String key, byte[] body);
}

具体产品内部再调用阿里云、AWS 或 MinIO SDK。这样替换厂商时,业务代码不用大面积修改。

三、用具体工厂封装厂商配置

具体工厂可以持有 endpoint、ak、sk、region、连接池等配置,并负责创建同一厂商下的多个客户端。

class AwsCloudFactory implements CloudFactory {
    public StorageClient createStorageClient() {
        return new AwsStorageClient(/* aws config */);
    }

    public QueueClient createQueueClient() {
        return new AwsQueueClient(/* aws config */);
    }
}

配置集中后,也更容易做测试替身和本地模拟。

四、不要把所有厂商能力硬塞进统一接口

不同云厂商可能有独有能力。抽象接口应该覆盖业务真正需要的稳定能力,不要为了追求统一,把所有厂商独有方法都放进公共接口。

如果某个能力只在一个厂商存在,可以考虑单独扩展接口、能力探测或在业务层明确限制。

五、用云客户端矩阵检查是否发生跨厂商混搭

存储与队列客户端都依赖 endpoint、region、凭证和厂商 SDK。若分别按字符串创建,可能得到 AliStorage 搭配 AwsQueue;由一个具体 CloudFactory 成套创建,可把同一业务上下文的厂商选择固定下来。

          Storage       Queue
Ali族      AliOSS        AliMNS
AWS族      S3            SQS
MinIO族    MinIOStorage  (若无 Queue 则不是完整同形产品族)
业务选择 AWSFactory
createStorage -> S3
createQueue -> SQS
不允许再独立选择 AliMNS

矩阵的每一行是一族兼容产品,每一列是一个产品等级。客户端选择一行对应的具体工厂,再从该工厂取得各列产品;如果允许调用方绕过工厂分别选择每个具体类,产品族一致性的结构性保证就消失了。

六、二维扩展成本与模式边界

评审维度本题结论
产品族不变量同一操作链中的相关客户端来自同一 provider、region/账号上下文。
新增一行(产品族)增加厂商适配产品和具体 CloudFactory。
新增一列(产品等级)新增短信/监控等公共能力会要求所有厂商工厂实现或明确拆分。
不应由工厂承担上传、发消息、事务补偿等业务编排。
退回更轻方案的条件厂商能力不对称严重时按能力拆接口/工厂,不制造大量“不支持”。

抽象工厂对所有变化并非都开放。它刻意固定列集合,以换取整行切换的一致性;如果系统最常发生的是新增列,工厂接口和所有行实现会频繁联动,此时应拆分工厂边界或选择组合方式。

七、落地验收清单

  1. 为每个具体工厂跑同一组契约测试,确认每个创建方法都返回正确产品等级。
  2. 给产品暴露 familyId() 仅用于测试,校验同一工厂创建的所有产品 familyId 一致。
  3. 加入一个假产品族,记录是否只新增一行实现和装配配置,而未修改旧工厂。
  4. 模拟新增一个产品等级,评估抽象接口和全部具体工厂的真实修改成本。
  5. 检查公共产品接口是否出现 AWS、MySQL、Windows 等具体实现词汇。
  6. 检查具体工厂是否只做组装与创建,没有吞入完整业务流程。
  7. 若产品持有连接或线程池,明确由工厂、容器还是调用方负责关闭。
  8. 若运行期按租户切换工厂,验证路由缓存、凭证刷新和租户隔离。

记忆钩子:抽象的是业务真正需要的交集,不是把所有厂商 SDK 求并集。

八、常见误区与追问

  • 误区:公共接口应覆盖所有厂商独有功能。 会让其他实现充满空方法或不支持异常。
  • 误区:同一厂商的所有客户端天然线程安全。 需按 SDK 文档和配置逐个判断。
  • 误区:工厂创建后无需考虑关闭。 客户端可能持有连接池和线程,生命周期必须明确。
  • 追问:某厂商没有 Queue 怎么办? 拆分可选能力、调整产品族,避免返回 null 产品。
  • 追问:数据库适配的产品等级可有哪些? SQL 方言、类型映射、分页生成器等相关组件。
  • 追问:凭证刷新放哪里? 可由具体客户端/凭证提供器负责,工厂只装配依赖,不吞入全部运行逻辑。

九、加强记忆

云厂商和数据库适配里,抽象工厂的价值是“统一一整套实现”。凡是存储、队列、方言、类型映射这些对象必须来自同一个提供方,就很适合用抽象工厂表达边界。