7、抽象工厂模式:产品族与产品等级结构

抽象工厂模式,说实话,是我在实际项目中用得最多的创建型模式之一。它解决了一个很现实的问题:当你的系统需要支持多套产品组合时,怎么设计才能不崩?

我记得有一次接手一个电商后台系统,需要同时对接支付宝和微信支付,每种支付方式又有自己的退款、对账、通知机制。如果每个类都单独 new,那代码简直没法看。抽象工厂模式就是专门处理这种「多套产品组合」的利器。

产品族与产品等级结构

在讲抽象工厂之前,得先搞清楚两个概念:产品族产品等级结构。这两个词听着绕,其实很好理解。

产品等级结构,说白了就是同一类产品的继承体系。比如「支付」是一个抽象接口,下面有「支付宝支付」「微信支付」这些具体实现。这就是一个产品等级结构。

产品族,指的是由同一个工厂生产的一组产品。比如「支付宝体系」包含支付宝支付、支付宝退款、支付宝对账——这些产品虽然功能不同,但都属于同一家。

你想想看,如果只有一种产品等级结构,用简单工厂或者工厂方法就够了。但一旦涉及多个产品等级结构之间的搭配,抽象工厂就派上用场了。

核心要点:抽象工厂模式关注的是「产品族」的创建,而不是单个产品。它保证同一个工厂生产出来的产品之间是兼容的。

抽象工厂模式的定义

抽象工厂模式(Abstract Factory Pattern)提供了一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

嗯,这里要注意:它和工厂方法模式最大的区别在于——工厂方法模式只生产一种产品,而抽象工厂模式生产一整套产品。

我个人的习惯是:当系统中存在多个产品等级结构,且这些产品需要组合使用时,优先考虑抽象工厂。比如 UI 组件库,Windows 风格下按钮、文本框、菜单是一套,Mac 风格下又是另一套。这两套之间不能混搭,对吧?

模式结构

抽象工厂模式包含以下几个角色:

  • AbstractFactory(抽象工厂):声明创建一组产品对象的接口
  • ConcreteFactory(具体工厂):实现抽象工厂,创建具体产品
  • AbstractProduct(抽象产品):为每种产品声明接口
  • ConcreteProduct(具体产品):实现抽象产品接口
  • Client(客户端):仅依赖抽象工厂和抽象产品
我的经验:客户端代码只和抽象层打交道,具体用哪套产品族由配置文件或运行时参数决定。这样新增一套产品族时,客户端代码一行都不用改。

SVG 结构图:抽象工厂模式核心逻辑

抽象工厂模式结构图 AbstractFactory +createProductA() +createProductB() ConcreteFactory1 创建产品族A1、B1 ConcreteFactory2 创建产品族A2、B2 AbstractProductA 支付接口 AbstractProductB 退款接口 ProductA1(支付宝支付) ProductA2(微信支付) ProductB1(支付宝退款) ProductB2(微信退款) 抽象工厂 具体工厂 抽象产品 具体产品

代码实现

咱们用支付场景来写个例子。假设系统需要支持支付宝和微信两套支付体系,每套体系包含支付和退款两个产品。

// 抽象产品:支付接口
public interface Payment {
    void pay(double amount);
}

// 抽象产品:退款接口
public interface Refund {
    void refund(double amount);
}

// 具体产品:支付宝支付
public class AlipayPayment implements Payment {
    @Override
    public void pay(double amount) {
        System.out.println("支付宝支付:" + amount + "元");
    }
}

// 具体产品:支付宝退款
public class AlipayRefund implements Refund {
    @Override
    public void refund(double amount) {
        System.out.println("支付宝退款:" + amount + "元");
    }
}

// 具体产品:微信支付
public class WechatPayment implements Payment {
    @Override
    public void pay(double amount) {
        System.out.println("微信支付:" + amount + "元");
    }
}

// 具体产品:微信退款
public class WechatRefund implements Refund {
    @Override
    public void refund(double amount) {
        System.out.println("微信退款:" + amount + "元");
    }
}

// 抽象工厂
public interface PaymentFactory {
    Payment createPayment();
    Refund createRefund();
}

// 具体工厂:支付宝工厂
public class AlipayFactory implements PaymentFactory {
    @Override
    public Payment createPayment() {
        return new AlipayPayment();
    }

    @Override
    public Refund createRefund() {
        return new AlipayRefund();
    }
}

// 具体工厂:微信工厂
public class WechatFactory implements PaymentFactory {
    @Override
    public Payment createPayment() {
        return new WechatPayment();
    }

    @Override
    public Refund createRefund() {
        return new WechatRefund();
    }
}

// 客户端使用
public class Client {
    public static void main(String[] args) {
        // 根据配置选择工厂
        PaymentFactory factory = new AlipayFactory();
        // 或者 factory = new WechatFactory();

        Payment payment = factory.createPayment();
        Refund refund = factory.createRefund();

        payment.pay(100.0);
        refund.refund(50.0);
    }
}
我的习惯:实际项目中,我会把工厂的选择逻辑放到一个配置类里,用枚举或者字符串从配置文件读取。这样切换产品族只需要改配置文件,代码完全不用动。

优缺点分析

优点 缺点
  • 保证产品族内产品的兼容性
  • 客户端与具体产品解耦
  • 符合开闭原则,新增产品族方便
  • 产品等级结构扩展相对容易
  • 新增产品等级结构困难(需要修改抽象工厂接口)
  • 类数量增多,系统复杂度上升
  • 抽象层较厚,理解成本高
避坑指南:我曾经在一个项目中过度使用抽象工厂,把三个不相关的产品硬塞到一个工厂里。结果后来要新增一个产品等级结构,所有工厂实现类都得改,差点没把我累死。抽象工厂只适合「产品族内产品天然相关」的场景,别强行凑。

适用场景

  • 系统需要独立于产品的创建、组合和表示
  • 系统需要配置多套产品族中的一套
  • 产品族内的产品需要一起使用,且这个约束需要被强制执行
  • 你希望提供一组产品类库,只暴露接口而不是实现

说白了,当你发现代码里有一堆 if-else 在判断「用哪套方案」,而且每套方案都涉及多个类的创建时,抽象工厂模式就是你的解药。

嗯,最后说一句:抽象工厂和工厂方法经常被拿来比较。我的经验是——如果只涉及一个产品等级结构,用工厂方法就够了。一旦涉及多个产品等级结构的组合,才考虑抽象工厂。别一上来就上抽象工厂,过度设计也是坑。


公众号:蓝海资料掘金营,微信deep3321