【设计模式】【创建型模式】抽象工厂模式(Abstract Factory)

发布于:2025-02-21 ⋅ 阅读:(18) ⋅ 点赞:(0)

👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD
🔥 2025本人正在沉淀中… 博客更新速度++
👍 欢迎点赞、收藏、关注,跟上我的更新节奏
🎵 当你的天空突然下了大雨,那是我在为你炸乌云

一、入门

什么是抽象工厂模式?

抽象工厂模式是一种创建型设计模式,它提供了一个接口,用于创建相关或依赖对象的家族,而不需要指定具体的类。
简单来说,抽象工厂模式是工厂方法模式的升级版,它能够创建一组相关的对象,而工厂方法模式只能创建一种对象。

为什么要抽象工厂模式?

假设你要购买一套家具(沙发、茶几、电视柜),并且希望这些家具的风格保持一致(比如现代风格或古典风格)。如果没有抽象工厂模式,下面是没有抽象工厂的实现:

// 现代风格沙发
class ModernSofa {
    public void lieOn() {
        System.out.println("躺在现代风格沙发上");
    }
}

// 古典风格沙发
class ClassicSofa {
    public void lieOn() {
        System.out.println("躺在古典风格沙发上");
    }
}

// 现代风格茶几
class ModernCoffeeTable {
    public void putOn() {
        System.out.println("在现代风格茶几上放东西");
    }
}

// 古典风格茶几
class ClassicCoffeeTable {
    public void putOn() {
        System.out.println("在古典风格茶几上放东西");
    }
}

// 客户端代码
public class FurnitureShop {
    public static void main(String[] args) {
        // 选择现代风格
        ModernSofa modernSofa = new ModernSofa();
        ModernCoffeeTable modernCoffeeTable = new ModernCoffeeTable();

        modernSofa.lieOn();
        modernCoffeeTable.putOn();

        // 选择古典风格
        ClassicSofa classicSofa = new ClassicSofa();
        ClassicCoffeeTable classicCoffeeTable = new ClassicCoffeeTable();

        classicSofa.lieOn();
        classicCoffeeTable.putOn();
    }
}

存在问题

  1. 风格一致性难以保证
    • 如果客户端代码不小心混用了不同风格的对象(比如现代风格沙发 + 古典风格茶几),会导致风格不一致。
ModernSofa sofa = new ModernSofa();
ClassicCoffeeTable table = new ClassicCoffeeTable(); // 风格不一致!
  1. 代码重复
    • 每次创建一组家具时,都需要重复编写类似的代码。
      ModernSofa sofa = new ModernSofa();
      ModernCoffeeTable table = new ModernCoffeeTable();
    • 如果家具种类增多(比如增加电视柜、餐桌),代码会变得更加冗长。
  2. 难以扩展
    • 如果新增一种风格(比如北欧风格),需要修改客户端代码,手动创建新的对象:
      NordicSofa sofa = new NordicSofa();
      NordicCoffeeTable table = new NordicCoffeeTable();
    • 这会违反开闭原则(对扩展开放,对修改关闭)。
  3. 客户端代码与具体类耦合
    • 客户端代码直接依赖具体的家具类(如ModernSofaClassicCoffeeTable),如果具体类发生变化(比如类名修改),客户端代码也需要跟着修改。

如何实现抽象工厂模式?

  1. 抽象工厂(Abstract Factory):声明一组创建产品对象的方法,每个方法对应一种产品。
  2. 具体工厂(Concrete Factory):实现抽象工厂接口,负责创建具体的产品对象。
  3. 抽象产品(Abstract Product):声明产品的接口。
  4. 具体产品(Concrete Product):实现抽象产品接口,定义具体产品的行为。

【案例】家具城 - 改
在这里插入图片描述
抽象产品(Abstract Product):沙发、茶几。

// 沙发
interface Sofa {
    void lieOn();
}

// 茶几
interface CoffeeTable {
    void putOn();
}

具体产品(Concrete Product): 现代沙发、古典风格沙发、现代风格茶几、古典风格茶几。

// 具体产品:现代风格沙发
class ModernSofa implements Sofa {
    public void lieOn() {
        System.out.println("躺在现代风格沙发上");
    }
}

// 具体产品:古典风格沙发
class ClassicSofa implements Sofa {
    public void lieOn() {
        System.out.println("躺在古典风格沙发上");
    }
}

// 具体产品:现代风格茶几
class ModernCoffeeTable implements CoffeeTable {
    public void putOn() {
        System.out.println("在现代风格茶几上放东西");
    }
}

// 具体产品:古典风格茶几
class ClassicCoffeeTable implements CoffeeTable {
    public void putOn() {
        System.out.println("在古典风格茶几上放东西");
    }
}

抽象工厂(Abstract Factory): 家具工厂

interface FurnitureFactory {
    Sofa createSofa();
    CoffeeTable createCoffeeTable();
}

具体工厂(Concrete Factory):现代风格家具工厂、古典风格家具工厂。

// 具体工厂:现代风格家具工厂
class ModernFurnitureFactory implements FurnitureFactory {
    public Sofa createSofa() {
        return new ModernSofa();
    }

    public CoffeeTable createCoffeeTable() {
        return new ModernCoffeeTable();
    }
}

// 具体工厂:古典风格家具工厂
class ClassicFurnitureFactory implements FurnitureFactory {
    public Sofa createSofa() {
        return new ClassicSofa();
    }

    public CoffeeTable createCoffeeTable() {
        return new ClassicCoffeeTable();
    }
}

客户端

public class FurnitureShop {
    private Sofa sofa;
    private CoffeeTable coffeeTable;

    public FurnitureShop(FurnitureFactory factory) {
        sofa = factory.createSofa();
        coffeeTable = factory.createCoffeeTable();
    }

    public void useFurniture() {
        sofa.lieOn();
        coffeeTable.putOn();
    }

    public static void main(String[] args) {
        // 选择现代风格家具工厂
        FurnitureFactory modernFactory = new ModernFurnitureFactory();
        FurnitureShop shop = new FurnitureShop(modernFactory);
        shop.useFurniture();

        // 选择古典风格家具工厂
        FurnitureFactory classicFactory = new ClassicFurnitureFactory();
        shop = new FurnitureShop(classicFactory);
        shop.useFurniture();
    }
}

抽象工厂模式和工厂方法的区别

场景 工厂方法模式 抽象工厂模式
创建目标 单个产品(如沙发) 一组相关产品(如沙发、茶几、电视柜)
风格一致性 无法保证 保证一组对象的风格一致
类的数量 每个产品需要一个工厂接口和具体工厂类 每个产品家族需要一个具体工厂类
客户端代码复杂度 需要分别调用多个工厂 只需调用一个工厂即可创建一组对象
适用场景 单一产品的创建 一组相关或依赖对象的创建

二、抽象工厂模式在框架源码中的运用

JDBC 中的 java.sql.Connection

JDBC(Java Database Connectivity)是Java中用于操作数据库的标准API,其中的 ConnectionStatement 等接口也体现了抽象工厂模式。

public class JDBCExample {
    public static void main(String[] args) throws Exception {
        // 获取数据库连接(抽象工厂)
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "user", "password");

        // 创建Statement(具体产品)
        Statement statement = connection.createStatement();

        // 执行查询
        ResultSet resultSet = statement.executeQuery("SELECT * FROM users");

        // 处理结果
        while (resultSet.next()) {
            System.out.println(resultSet.getString("username"));
        }
    }
}

抽象工厂模式的应用

  • 抽象工厂Connection 是一个抽象工厂,定义了创建 StatementPreparedStatement等方法。
  • 具体工厂:不同的数据库驱动(如MySQL、Oracle)提供了具体的 Connection 实现。
  • 产品家族StatementPreparedStatementResultSet 是相关的产品,用于执行SQL语句和处理结果。

Spring 框架中的 BeanFactory

Spring 框架的核心容器 BeanFactory 也是一个典型的抽象工厂模式应用。

public class SpringExample {
    public static void main(String[] args) {
        // 加载Spring配置文件
        BeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));

        // 获取Bean(具体产品)
        MyService myService = (MyService) factory.getBean("myService");
        myService.execute();
    }
}

抽象工厂模式的应用

  • 抽象工厂BeanFactory 是一个抽象工厂,定义了获取Bean的方法(如getBean)。
  • 具体工厂XmlBeanFactory 是一个具体工厂,负责从XML配置文件中创建和管理Bean。
  • 产品家族:Spring容器管理的Bean(如 MyService)是相关的产品,它们可能相互依赖或协作。

三、总结

抽象工厂模式的优点

  1. 保证产品家族的一致性
    • 抽象工厂模式确保创建的对象是同一家族的,比如现代风格的沙发、茶几、电视柜,避免了风格不一致的问题。
  2. 解耦客户端代码
    • 客户端代码只依赖于抽象工厂和抽象产品接口,不依赖于具体的实现类,符合依赖倒置原则。
  3. 符合开闭原则
    • 新增产品家族(如新增北欧风格家具)时,只需增加新的具体工厂类,不需要修改现有代码。
  4. 简化客户端代码
    • 客户端不需要关心具体对象的创建细节,只需要调用工厂方法即可。
  5. 提高代码可维护性
    • 将对象的创建逻辑集中到工厂中,便于统一管理和维护。

抽象工厂模式的缺点

  1. 类的数量增加
    • 每增加一个产品家族,就需要增加一组具体工厂类和具体产品类,可能导致类的数量爆炸。
  2. 扩展产品类型困难
    • 如果需要在现有产品家族中新增产品类型(比如在家具家族中新增“餐桌”),需要修改抽象工厂接口及其所有具体工厂类,违反了开闭原则。
  3. 复杂性增加
    • 对于简单的对象创建场景,使用抽象工厂模式可能会引入不必要的复杂性。
  4. 理解难度较高
    • 抽象工厂模式涉及多个抽象层(抽象工厂、抽象产品、具体工厂、具体产品),初学者可能难以理解。

抽象工厂模式的适用场景

抽象工厂模式适用于以下场景:

  1. 需要创建一组相关或依赖的对象:
    • 比如跨平台的UI组件(Windows按钮、Mac按钮)、家具家族(现代风格沙发、茶几)、数据库访问对象(ConnectionStatement)等。
  2. 需要保证产品家族的一致性:
    • 比如确保所有UI组件都是同一风格,或者所有家具都是同一主题。
  3. 系统需要独立于产品的创建、组合和表示:
    • 比如框架或库的设计,希望将对象的创建逻辑与使用逻辑分离。
  4. 需要支持多种产品家族:
    • 比如支持多种数据库(MySQL、Oracle)、多种操作系统(Windows、Mac)、多种主题(现代、古典)等。
  5. 希望通过配置或依赖注入动态切换产品家族:
    • 比如Spring框架中通过配置文件切换具体的Bean实现。