公众号【CTO成长日记】

Java 设计模式之工厂模式:对象创建的艺术(含 UML 图解)

在软件开发中,对象的创建是最基础也最频繁的操作。如果直接在代码中到处使用new关键字创建对象,会导致代码耦合度高、扩展性差。工厂模式(Factory Pattern)通过将对象创建的逻辑封装在专门的工厂类中,实现了 “创建与使用分离”,是解决对象创建问题的经典方案。

工厂模式并非单一模式,而是一个模式家族,包含简单工厂模式工厂方法模式抽象工厂模式三种形式。本文将详细讲解这三种模式的实现与应用场景。

一、工厂模式的核心思想

工厂模式属于创建型设计模式,它的核心思想是:将对象的创建过程封装起来,让客户端无需关心对象的具体创建细节,只需通过工厂获取所需对象

生活中的工厂模式例子随处可见:

  • 汽车工厂生产不同品牌的汽车,消费者无需知道汽车的生产细节,直接从工厂提车即可
  • 披萨店(工厂)制作不同口味的披萨,顾客只需点单(调用工厂方法),无需自己动手制作

工厂模式的核心价值在于:

  • 降低耦合:客户端与具体产品解耦,只依赖工厂和抽象产品
  • 提高扩展性:新增产品时只需扩展工厂,无需修改客户端
  • 统一管理:对象创建逻辑集中在工厂,便于维护和控制

二、简单工厂模式(Simple Factory)

简单工厂模式是工厂模式中最基础的形式,它通过一个工厂类根据传入的参数决定创建哪种产品对象。

核心角色

  1. 抽象产品(Product):定义产品的公共接口
  2. 具体产品(Concrete Product):实现抽象产品接口,是工厂创建的目标
  3. 工厂(Factory):根据参数创建具体产品的实例

UML 类图

+------------------+
|    Product       |
+------------------+
| + operation(): void |
+------------------+
        ^
        |
+------------------+  +------------------+
| ConcreteProductA |  | ConcreteProductB |
+------------------+  +------------------+
| + operation(): void |  | + operation(): void |
+------------------+  +------------------+
        ^                     ^
        |                     |
+------------------+
|    Factory       |
+------------------+
| + createProduct(type): Product |
+------------------+

代码实现:计算器工厂

以简单计算器为例,实现一个能创建加法、减法运算器的简单工厂:

1. 抽象产品(运算接口)
/**
 * 抽象产品:运算接口
 */
public interface Operation {
    double calculate(double a, double b);
}
2. 具体产品(加法、减法实现)
/**
 * 具体产品:加法运算
 */
public class AddOperation implements Operation {
    @Override
    public double calculate(double a, double b) {
        return a + b;
    }
}

/**
 * 具体产品:减法运算
 */
public class SubtractOperation implements Operation {
    @Override
    public double calculate(double a, double b) {
        return a - b;
    }
}
3. 工厂类(运算器工厂)
/**
 * 简单工厂:运算器工厂
 */
public class OperationFactory {
    /**
     * 根据类型创建运算器
     * @param type 运算类型:"+" "-"
     * @return 具体运算器实例
     */
    public static Operation createOperation(String type) {
        Operation operation = null;
        switch (type) {
            case "+":
                operation = new AddOperation();
                break;
            case "-":
                operation = new SubtractOperation();
                break;
            default:
                throw new IllegalArgumentException("不支持的运算类型");
        }
        return operation;
    }
}
4. 客户端使用
public class Client {
    public static void main(String[] args) {
        // 从工厂获取加法运算器
        Operation addOp = OperationFactory.createOperation("+");
        System.out.println("3 + 5 = " + addOp.calculate(3, 5)); // 8.0
        
        // 从工厂获取减法运算器
        Operation subOp = OperationFactory.createOperation("-");
        System.out.println("10 - 4 = " + subOp.calculate(10, 4)); // 6.0
    }
}

简单工厂模式的优缺点

  • 优点:实现简单,客户端无需知道产品创建细节
  • 缺点:违反开闭原则,新增产品需修改工厂类的判断逻辑,工厂类职责过重

三、工厂方法模式(Factory Method)

为了解决简单工厂模式的缺陷,工厂方法模式将工厂抽象化,为每个产品创建对应的工厂类,使新增产品时只需扩展工厂,无需修改原有代码。

核心角色

  1. 抽象产品(Product):定义产品的公共接口
  2. 具体产品(Concrete Product):实现抽象产品接口
  3. 抽象工厂(Abstract Factory):定义创建产品的接口
  4. 具体工厂(Concrete Factory):实现抽象工厂接口,创建具体产品

UML 类图

+------------------+       +------------------+
|    Product       |       |  AbstractFactory |
+------------------+       +------------------+
| + operation(): void |       | + createProduct(): Product |
+------------------+       +------------------+
        ^                           ^
        |                           |
+------------------+       +------------------+
| ConcreteProductA |<----->|  FactoryA        |
+------------------+       +------------------+
| + operation(): void |       | + createProduct(): Product |
+------------------+       +------------------+

+------------------+       +------------------+
| ConcreteProductB |<----->|  FactoryB        |
+------------------+       +------------------+
| + operation(): void |       | + createProduct(): Product |
+------------------+       +------------------+

代码实现:日志记录器工厂

以日志记录为例,实现文件日志和数据库日志的工厂方法模式:

1. 抽象产品(日志接口)
/**
 * 抽象产品:日志记录器
 */
public interface Logger {
    void log(String message);
}
2. 具体产品(文件日志、数据库日志)
/**
 * 具体产品:文件日志记录器
 */
public class FileLogger implements Logger {
    @Override
    public void log(String message) {
        System.out.println("文件日志:" + message);
    }
}

/**
 * 具体产品:数据库日志记录器
 */
public class DatabaseLogger implements Logger {
    @Override
    public void log(String message) {
        System.out.println("数据库日志:" + message);
    }
}
3. 抽象工厂(日志工厂接口)
/**
 * 抽象工厂:日志工厂
 */
public interface LoggerFactory {
    Logger createLogger();
}
4. 具体工厂(文件日志工厂、数据库日志工厂)
/**
 * 具体工厂:文件日志工厂
 */
public class FileLoggerFactory implements LoggerFactory {
    @Override
    public Logger createLogger() {
        // 可以在这里添加文件日志的初始化逻辑
        return new FileLogger();
    }
}

/**
 * 具体工厂:数据库日志工厂
 */
public class DatabaseLoggerFactory implements LoggerFactory {
    @Override
    public Logger createLogger() {
        // 可以在这里添加数据库连接等初始化逻辑
        return new DatabaseLogger();
    }
}
5. 客户端使用
public class Client {
    public static void main(String[] args) {
        // 创建文件日志工厂和日志记录器
        LoggerFactory fileFactory = new FileLoggerFactory();
        Logger fileLogger = fileFactory.createLogger();
        fileLogger.log("系统启动成功"); // 文件日志:系统启动成功
        
        // 创建数据库日志工厂和日志记录器
        LoggerFactory dbFactory = new DatabaseLoggerFactory();
        Logger dbLogger = dbFactory.createLogger();
        dbLogger.log("用户登录"); // 数据库日志:用户登录
    }
}

工厂方法模式的优缺点

  • 优点:符合开闭原则,新增产品只需新增对应的产品类和工厂类;单一职责,每个工厂只负责创建一种产品
  • 缺点:类数量增多,系统复杂度提高;客户端需要知道具体工厂才能获取对应产品

四、抽象工厂模式(Abstract Factory)

当需要创建一系列相互关联或依赖的产品时,抽象工厂模式应运而生。它提供一个接口,用于创建多个产品族中的产品对象。

核心概念

  • 产品族:同一工厂生产的、功能相关联的一组产品(如华为工厂生产的手机、平板)
  • 产品等级结构:同一类产品的不同实现(如手机有华为手机、苹果手机)

核心角色

  1. 抽象产品(Abstract Product):多个产品等级结构的接口
  2. 具体产品(Concrete Product):实现抽象产品接口
  3. 抽象工厂(Abstract Factory):声明创建多个产品族的方法
  4. 具体工厂(Concrete Factory):实现抽象工厂,创建同一产品族的产品

UML 类图

+------------------+  +------------------+
|    Phone         |  |    Tablet        |
+------------------+  +------------------+
| + call(): void   |  | + browse(): void |
+------------------+  +------------------+
        ^                     ^
        |                     |
+------------------+  +------------------+
| HuaweiPhone      |  | HuaweiTablet     |
+------------------+  +------------------+
| + call(): void   |  | + browse(): void |
+------------------+  +------------------+

+------------------+  +------------------+
| IPhone           |  | IPad             |
+------------------+  +------------------+
| + call(): void   |  | + browse(): void |
+------------------+  +------------------+

        ^                     ^
        |                     |
+------------------+
|  AbstractFactory |
+------------------+
| + createPhone(): Phone    |
| + createTablet(): Tablet  |
+------------------+
        ^
        |
+------------------+  +------------------+
| HuaweiFactory    |  | AppleFactory     |
+------------------+  +------------------+
| + createPhone(): Phone    |  | + createPhone(): Phone    |
| + createTablet(): Tablet  |  | + createTablet(): Tablet  |
+------------------+  +------------------+

代码实现:电子设备工厂

以华为和苹果的产品族为例,实现能生产手机和平板的抽象工厂:

1. 抽象产品(手机、平板接口)
/**
 * 抽象产品:手机
 */
public interface Phone {
    void call();
}

/**
 * 抽象产品:平板
 */
public interface Tablet {
    void browse();
}
2. 具体产品(华为 / 苹果的手机和平板)
/**
 * 具体产品:华为手机
 */
public class HuaweiPhone implements Phone {
    @Override
    public void call() {
        System.out.println("用华为手机打电话");
    }
}

/**
 * 具体产品:华为平板
 */
public class HuaweiTablet implements Tablet {
    @Override
    public void browse() {
        System.out.println("用华为平板浏览网页");
    }
}

/**
 * 具体产品:苹果手机
 */
public class IPhone implements Phone {
    @Override
    public void call() {
        System.out.println("用iPhone打电话");
    }
}

/**
 * 具体产品:苹果平板
 */
public class IPad implements Tablet {
    @Override
    public void browse() {
        System.out.println("用iPad浏览网页");
    }
}
3. 抽象工厂(电子设备工厂接口)
/**
 * 抽象工厂:电子设备工厂
 */
public interface ElectronicFactory {
    Phone createPhone();  // 生产手机
    Tablet createTablet();// 生产平板
}
4. 具体工厂(华为工厂、苹果工厂)
/**
 * 具体工厂:华为工厂
 */
public class HuaweiFactory implements ElectronicFactory {
    @Override
    public Phone createPhone() {
        return new HuaweiPhone();
    }
    
    @Override
    public Tablet createTablet() {
        return new HuaweiTablet();
    }
}

/**
 * 具体工厂:苹果工厂
 */
public class AppleFactory implements ElectronicFactory {
    @Override
    public Phone createPhone() {
        return new IPhone();
    }
    
    @Override
    public Tablet createTablet() {
        return new IPad();
    }
}
5. 客户端使用
public class Client {
    public static void main(String[] args) {
        // 使用华为工厂生产华为产品族
        ElectronicFactory huaweiFactory = new HuaweiFactory();
        Phone huaweiPhone = huaweiFactory.createPhone();
        Tablet huaweiTablet = huaweiFactory.createTablet();
        huaweiPhone.call();    // 用华为手机打电话
        huaweiTablet.browse(); // 用华为平板浏览网页
        
        // 使用苹果工厂生产苹果产品族
        ElectronicFactory appleFactory = new AppleFactory();
        Phone iPhone = appleFactory.createPhone();
        Tablet iPad = appleFactory.createTablet();
        iPhone.call();         // 用iPhone打电话
        iPad.browse();         // 用iPad浏览网页
    }
}

抽象工厂模式的优缺点

  • 优点:便于管理同一产品族的产品;保证产品族内产品的兼容性;符合开闭原则(扩展产品族只需新增工厂)
  • 缺点:扩展新的产品等级结构困难(如新增手表产品,需修改所有工厂接口和实现)

五、三种工厂模式的对比与选择

模式 核心特点 适用场景 优缺点对比
简单工厂 一个工厂类创建所有产品 产品种类少,变化不频繁 实现简单但违反开闭原则
工厂方法 一个工厂对应一个产品 产品种类较多,需要灵活扩展 符合开闭原则但类数量增多
抽象工厂 一个工厂创建一个产品族 需要创建相互关联的产品组,强调产品族兼容性 适合产品族扩展,但难扩展新产品

选择建议

  • 简单场景用简单工厂,快速实现
  • 单一产品扩展用工厂方法,兼顾灵活与简单
  • 多产品族场景用抽象工厂,保证产品关联性

六、工厂模式在 Java 中的应用

  1. JDK 中的应用
    • java.util.Calendar:通过getInstance()方法(简单工厂)创建日历实例
    • java.sql.DriverManager:通过getConnection()获取不同数据库连接(工厂方法)
    • java.net.URLStreamHandlerFactory:创建不同协议的流处理器(抽象工厂)
  2. 框架中的应用
    • Spring 的BeanFactory:通过工厂模式创建和管理 Bean 对象
    • MyBatis 的SqlSessionFactory:创建SqlSession对象(工厂方法)

总结

工厂模式通过封装对象创建逻辑,完美诠释了 “创建与使用分离” 的设计思想。从简单工厂到抽象工厂,模式的复杂度逐渐增加,但灵活性和适用性也随之提升。

在实际开发中,不必盲目追求复杂模式,应根据业务场景选择最合适的实现:简单场景用简单工厂提高开发效率,复杂场景用工厂方法或抽象工厂保证扩展性。记住,设计模式的核心是解决问题,而非炫技。

掌握工厂模式,不仅能写出更优雅的代码,更能培养 “封装变化” 的设计思维 —— 这正是优秀程序员与普通程序员的重要区别。

原文:CTO成长日记

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐