设计模式——工厂方法模式

工厂方法模式 (Factory Method Pattern)

什么是工厂方法模式?

工厂方法模式是一种创建型设计模式,它定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。

简单来说:工厂方法模式就是用一个"工厂"类来创建对象,而不是直接使用new关键字。

生活中的例子

想象一家汽车制造厂

  • 抽象产品:汽车(所有汽车都有共同的特征)
  • 具体产品:奔驰、宝马、奥迪等不同品牌的汽车
  • 抽象工厂:汽车工厂(定义了制造汽车的方法)
  • 具体工厂:奔驰工厂、宝马工厂、奥迪工厂(分别制造对应品牌的汽车)

为什么需要工厂方法模式?

传统方式的问题

java 复制代码
// 直接使用new创建对象
Car car = new Benz();

问题

  1. 客户端与具体类耦合:如果需要换成宝马,必须修改代码
  2. 创建逻辑分散:对象的创建逻辑散落在代码各处
  3. 扩展困难:新增车型时需要修改多处代码

工厂方法模式的优势

java 复制代码
// 使用工厂创建对象
Car car = carFactory.createCar();

优势

  1. 解耦:客户端不需要知道具体创建哪个对象
  2. 统一管理:对象的创建逻辑集中在工厂类中
  3. 易于扩展:新增产品时,只需新增对应的工厂类

工厂方法模式的结构

复制代码
┌─────────────────────┐
│    Product (产品)    │  抽象产品
├─────────────────────┤
│ + operation(): void │
└──────────┬──────────┘
           │ 继承
           ├──┬──────────────────┬──────────────┐
           │                    │              │
┌──────────┴──────┐  ┌───────────┴───────┐  ┌───┴────────┐
│ ConcreteProduct1 │  │ ConcreteProduct2 │  │ ...       │  具体产品
└─────────────────┘  └───────────────────┘  └────────────┘

┌─────────────────────┐
│    Creator (工厂)    │  抽象工厂
├─────────────────────┤
│ + factoryMethod():  │
│   Product           │
│ + anOperation():    │
│   void              │
└──────────┬──────────┘
           │ 继承
           ├──┬──────────────────┬──────────────┐
           │                    │              │
┌──────────┴──────┐  ┌───────────┴───────┐  ┌───┴────────┐
│ ConcreteCreator1 │  │ ConcreteCreator2 │  │ ...       │  具体工厂
└─────────────────┘  └───────────────────┘  └────────────┘

代码示例

1. 定义抽象产品

java 复制代码
/**
 * 抽象产品:日志记录器
 */
public interface Logger {
    /**
     * 记录日志
     */
    void log(String message);
    
    /**
     * 记录错误
     */
    void error(String message);
}

2. 定义具体产品

java 复制代码
/**
 * 具体产品:控制台日志记录器
 */
public class ConsoleLogger implements Logger {
    @Override
    public void log(String message) {
        System.out.println("[INFO] " + message);
    }
    
    @Override
    public void error(String message) {
        System.out.println("[ERROR] " + message);
    }
}

/**
 * 具体产品:文件日志记录器
 */
public class FileLogger implements Logger {
    @Override
    public void log(String message) {
        System.out.println("[FILE LOG] " + message);
    }
    
    @Override
    public void error(String message) {
        System.out.println("[FILE ERROR] " + message);
    }
}

/**
 * 具体产品:数据库日志记录器
 */
public class DatabaseLogger implements Logger {
    @Override
    public void log(String message) {
        System.out.println("[DB LOG] " + message);
    }
    
    @Override
    public void error(String message) {
        System.out.println("[DB ERROR] " + message);
    }
}

3. 定义抽象工厂

java 复制代码
/**
 * 抽象工厂:日志记录器工厂
 */
public abstract class LoggerFactory {
    /**
     * 工厂方法:创建日志记录器
     */
    public abstract Logger createLogger();
    
    /**
     * 模板方法:记录日志消息
     */
    public void logMessage(String message) {
        Logger logger = createLogger();
        logger.log(message);
    }
    
    /**
     * 模板方法:记录错误消息
     */
    public void logError(String message) {
        Logger logger = createLogger();
        logger.error(message);
    }
}

4. 定义具体工厂

java 复制代码
/**
 * 具体工厂:控制台日志记录器工厂
 */
public class ConsoleLoggerFactory extends LoggerFactory {
    @Override
    public Logger createLogger() {
        return new ConsoleLogger();
    }
}

/**
 * 具体工厂:文件日志记录器工厂
 */
public class FileLoggerFactory extends LoggerFactory {
    @Override
    public Logger createLogger() {
        return new FileLogger();
    }
}

/**
 * 具体工厂:数据库日志记录器工厂
 */
public class DatabaseLoggerFactory extends LoggerFactory {
    @Override
    public Logger createLogger() {
        return new DatabaseLogger();
    }
}

5. 使用工厂

java 复制代码
/**
 * 工厂方法模式测试类
 * 演示如何使用工厂方法模式创建不同类型的日志记录器
 */
public class FactoryMethodTest {
    
    public static void main(String[] args) {
        System.out.println("=== 工厂方法模式测试 ===\n");
        
        // 测试控制台日志记录器
        System.out.println("--- 测试控制台日志记录器 ---");
        LoggerFactory consoleFactory = new ConsoleLoggerFactory();
        Logger consoleLogger = consoleFactory.createLogger();
        consoleLogger.log("应用程序启动");
        consoleLogger.error("发生了一个错误");
        
        System.out.println();
        
        // 测试文件日志记录器
        System.out.println("--- 测试文件日志记录器 ---");
        LoggerFactory fileFactory = new FileLoggerFactory();
        Logger fileLogger = fileFactory.createLogger();
        fileLogger.log("用户登录成功");
        fileLogger.error("数据库连接失败");
        
        System.out.println();
        
        // 测试数据库日志记录器
        System.out.println("--- 测试数据库日志记录器 ---");
        LoggerFactory dbFactory = new DatabaseLoggerFactory();
        Logger dbLogger = dbFactory.createLogger();
        dbLogger.log("订单创建成功");
        dbLogger.error("支付网关超时");
        
        System.out.println("\n=== 使用模板方法 ===");
        
        // 使用模板方法
        consoleFactory.logMessage("使用模板方法记录日志");
        consoleFactory.logError("使用模板方法记录错误");
        
        System.out.println();
        
        fileFactory.logMessage("使用模板方法记录日志到文件");
        fileFactory.logError("使用模板方法记录错误到文件");
        
        System.out.println("\n=== 扩展性测试 ===");
        System.out.println("如果需要新增一种日志记录器(如:网络日志),");
        System.out.println("只需:");
        System.out.println("1. 创建 NetworkLogger 实现 Logger 接口");
        System.out.println("2. 创建 NetworkLoggerFactory 继承 LoggerFactory");
        System.out.println("3. 无需修改任何现有代码,符合开闭原则!");
    }
}

工厂方法模式的优点

  1. 符合开闭原则:新增产品时,只需新增对应的工厂类,无需修改现有代码
  2. 代码解耦:客户端与具体产品解耦,只依赖抽象产品
  3. 职责单一:每个具体工厂只负责创建一种产品
  4. 易于测试:可以轻松替换具体工厂进行测试

工厂方法模式的缺点

  1. 类数量增加:每增加一个产品,就需要增加一个具体工厂类
  2. 系统复杂度增加:引入了抽象层,增加了系统的理解难度
  3. 简单场景过度设计:如果产品种类很少,使用工厂方法模式可能显得过于复杂

适用场景

  1. 不知道具体要创建哪个产品:客户端只知道需要产品,但不知道具体是哪个
  2. 希望将创建逻辑延迟到子类:让子类决定创建哪个具体产品
  3. 产品种类较多:有多种产品需要创建,且可能继续增加
  4. 需要解耦客户端和具体产品:客户端不依赖具体产品类

常见应用场景

  • 日志框架:不同类型的日志记录器(文件日志、控制台日志、数据库日志)
  • 数据库连接:不同类型的数据库连接(MySQL、Oracle、PostgreSQL)
  • 支付系统:不同的支付方式(支付宝、微信、银行卡)
  • 消息队列:不同的消息队列实现(RabbitMQ、Kafka、ActiveMQ)

与简单工厂的区别

特性 简单工厂 工厂方法
工厂类数量 1个工厂类 多个工厂类
创建逻辑 集中在一个工厂类 分散在各个具体工厂类
扩展性 修改工厂类 新增工厂类
符合开闭原则
复杂度 简单 较复杂

使用建议

  • 产品种类少且固定:使用简单工厂
  • 产品种类多且可能增加:使用工厂方法模式
  • 需要遵循开闭原则:使用工厂方法模式
  • 创建逻辑复杂:使用工厂方法模式,将创建逻辑分散到各个工厂类中

注意事项

⚠️ 工厂方法模式虽然很好,但不要滥用:

  • 如果产品种类很少(1-2个),直接使用new可能更简单
  • 如果创建逻辑很简单,可以考虑使用简单工厂
  • 工厂方法模式会增加类的数量,要权衡利弊
相关推荐
杨充1 天前
10.可测试性实战设计
设计模式·开源·代码规范
杨充1 天前
9.重构十二式的实战
设计模式·开源·代码规范
杨充1 天前
6.设计原则的全景图
设计模式·开源·全栈
杨充1 天前
2.面向对象的特性
设计模式
杨充1 天前
7.SOLID原则案例汇
设计模式·开源·全栈
杨充1 天前
8.反模式与坏味道
设计模式·开源·代码规范
杨充1 天前
3.接口vs抽象类比较
设计模式
咖啡八杯1 天前
文法、BNF与AST
java·设计模式·解释器模式·ast·文法
咖啡八杯1 天前
GoF设计模式——解释器模式
java·后端·spring·设计模式