设计模式之:工厂方法模式

文章目录

    • 什么是工厂方法模式?
    • 核心思想
    • 模式结构
    • 完整示例:日志记录系统
      • [1. 定义抽象产品](#1. 定义抽象产品)
      • [2. 实现具体产品](#2. 实现具体产品)
      • [3. 定义抽象工厂](#3. 定义抽象工厂)
      • [4. 实现具体工厂](#4. 实现具体工厂)
      • [5. 客户端使用示例](#5. 客户端使用示例)
      • [6. 进阶示例:配置化的日志工厂](#6. 进阶示例:配置化的日志工厂)
    • 工厂方法模式的优点
      • [1. 符合开闭原则](#1. 符合开闭原则)
      • [2. 客户端与具体产品解耦](#2. 客户端与具体产品解耦)
      • [3. 提高代码的可测试性](#3. 提高代码的可测试性)
    • 工厂方法模式的缺点
      • [1. 类的数量增加](#1. 类的数量增加)
      • [2. 增加了系统的复杂性](#2. 增加了系统的复杂性)
    • 适用场景
    • 与简单工厂模式的对比
    • 最佳实践
      • [1. 使用依赖注入](#1. 使用依赖注入)
      • [2. 结合配置文件](#2. 结合配置文件)
      • [3. 使用泛型增强类型安全](#3. 使用泛型增强类型安全)
    • 总结

什么是工厂方法模式?

工厂方法模式(Factory Method Pattern)是一种经典的创建型设计模式,它定义了一个创建对象的接口,但让子类决定要实例化哪一个类。工厂方法让类的实例化推迟到子类,完美遵循了"开闭原则",实现了对象创建的可扩展性。

核心思想

工厂方法模式的核心是:定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类。工厂方法让类的实例化延迟到子类进行。

模式结构

工厂方法模式包含四个核心角色:

  1. 抽象产品(Product):定义产品的接口
  2. 具体产品(Concrete Product):实现抽象产品接口的具体类
  3. 抽象工厂(Creator):声明工厂方法,返回产品对象
  4. 具体工厂(Concrete Creator):重写工厂方法,返回具体产品实例

完整示例:日志记录系统

让我们通过一个完整的日志记录系统来深入理解工厂方法模式。

1. 定义抽象产品

java 复制代码
/**
 * 日志记录器接口 - 抽象产品角色
 * 定义日志记录的基本操作
 */
public interface Logger {
    /**
     * 记录信息日志
     * @param message 日志信息
     */
    void info(String message);
    
    /**
     * 记录错误日志
     * @param message 错误信息
     */
    void error(String message);
    
    /**
     * 记录警告日志
     * @param message 警告信息
     */
    void warn(String message);
    
    /**
     * 记录调试日志
     * @param message 调试信息
     */
    void debug(String message);
}

2. 实现具体产品

java 复制代码
/**
 * 文件日志记录器 - 具体产品角色
 * 将日志记录到文件中
 */
public class FileLogger implements Logger {
    private String filePath;
    
    public FileLogger(String filePath) {
        this.filePath = filePath;
        System.out.println("初始化文件日志记录器,文件路径: " + filePath);
    }
    
    @Override
    public void info(String message) {
        String log = String.format("[INFO] %s %s", getTimestamp(), message);
        writeToFile(log);
    }
    
    @Override
    public void error(String message) {
        String log = String.format("[ERROR] %s %s", getTimestamp(), message);
        writeToFile(log);
    }
    
    @Override
    public void warn(String message) {
        String log = String.format("[WARN] %s %s", getTimestamp(), message);
        writeToFile(log);
    }
    
    @Override
    public void debug(String message) {
        String log = String.format("[DEBUG] %s %s", getTimestamp(), message);
        writeToFile(log);
    }
    
    private void writeToFile(String log) {
        // 模拟写入文件操作
        System.out.println("写入文件[" + filePath + "]: " + log);
    }
    
    private String getTimestamp() {
        return java.time.LocalDateTime.now().toString();
    }
}

/**
 * 控制台日志记录器 - 具体产品角色
 * 将日志输出到控制台
 */
public class ConsoleLogger implements Logger {
    @Override
    public void info(String message) {
        System.out.println("\u001B[32m[INFO] " + getTimestamp() + " " + message + "\u001B[0m");
    }
    
    @Override
    public void error(String message) {
        System.out.println("\u001B[31m[ERROR] " + getTimestamp() + " " + message + "\u001B[0m");
    }
    
    @Override
    public void warn(String message) {
        System.out.println("\u001B[33m[WARN] " + getTimestamp() + " " + message + "\u001B[0m");
    }
    
    @Override
    public void debug(String message) {
        System.out.println("\u001B[36m[DEBUG] " + getTimestamp() + " " + message + "\u001B[0m");
    }
    
    private String getTimestamp() {
        return java.time.LocalDateTime.now().toString();
    }
}

/**
 * 数据库日志记录器 - 具体产品角色
 * 将日志记录到数据库中
 */
public class DatabaseLogger implements Logger {
    private String dataSource;
    
    public DatabaseLogger(String dataSource) {
        this.dataSource = dataSource;
        System.out.println("初始化数据库日志记录器,数据源: " + dataSource);
    }
    
    @Override
    public void info(String message) {
        String log = String.format("INSERT INTO logs (level, message, timestamp) VALUES ('INFO', '%s', '%s')", 
                                 message, getTimestamp());
        executeSQL(log);
    }
    
    @Override
    public void error(String message) {
        String log = String.format("INSERT INTO logs (level, message, timestamp) VALUES ('ERROR', '%s', '%s')", 
                                 message, getTimestamp());
        executeSQL(log);
    }
    
    @Override
    public void warn(String message) {
        String log = String.format("INSERT INTO logs (level, message, timestamp) VALUES ('WARN', '%s', '%s')", 
                                 message, getTimestamp());
        executeSQL(log);
    }
    
    @Override
    public void debug(String message) {
        String log = String.format("INSERT INTO logs (level, message, timestamp) VALUES ('DEBUG', '%s', '%s')", 
                                 message, getTimestamp());
        executeSQL(log);
    }
    
    private void executeSQL(String sql) {
        // 模拟执行SQL
        System.out.println("执行SQL: " + sql);
    }
    
    private String getTimestamp() {
        return java.time.LocalDateTime.now().toString();
    }
}

3. 定义抽象工厂

java 复制代码
/**
 * 日志记录器工厂接口 - 抽象工厂角色
 * 声明工厂方法,由子类实现具体创建逻辑
 */
public interface LoggerFactory {
    /**
     * 工厂方法 - 创建日志记录器
     * @return 日志记录器实例
     */
    Logger createLogger();
    
    /**
     * 使用日志记录器记录信息
     * @param message 日志信息
     */
    default void logInfo(String message) {
        Logger logger = createLogger();
        logger.info(message);
    }
    
    /**
     * 使用日志记录器记录错误
     * @param message 错误信息
     */
    default void logError(String message) {
        Logger logger = createLogger();
        logger.error(message);
    }
}

4. 实现具体工厂

java 复制代码
/**
 * 文件日志记录器工厂 - 具体工厂角色
 * 负责创建文件日志记录器
 */
public class FileLoggerFactory implements LoggerFactory {
    private String filePath;
    
    public FileLoggerFactory(String filePath) {
        this.filePath = filePath;
    }
    
    @Override
    public Logger createLogger() {
        return new FileLogger(filePath);
    }
}

/**
 * 控制台日志记录器工厂 - 具体工厂角色
 * 负责创建控制台日志记录器
 */
public class ConsoleLoggerFactory implements LoggerFactory {
    @Override
    public Logger createLogger() {
        return new ConsoleLogger();
    }
}

/**
 * 数据库日志记录器工厂 - 具体工厂角色
 * 负责创建数据库日志记录器
 */
public class DatabaseLoggerFactory implements LoggerFactory {
    private String dataSource;
    
    public DatabaseLoggerFactory(String dataSource) {
        this.dataSource = dataSource;
    }
    
    @Override
    public Logger createLogger() {
        return new DatabaseLogger(dataSource);
    }
}

5. 客户端使用示例

java 复制代码
/**
 * 应用程序类 - 客户端代码
 * 演示工厂方法模式的使用
 */
public class Application {
    public static void main(String[] args) {
        System.out.println("=== 工厂方法模式演示 ===\n");
        
        // 使用控制台日志
        System.out.println("1. 使用控制台日志记录器:");
        useConsoleLogger();
        
        // 使用文件日志
        System.out.println("\n2. 使用文件日志记录器:");
        useFileLogger();
        
        // 使用数据库日志
        System.out.println("\n3. 使用数据库日志记录器:");
        useDatabaseLogger();
        
        // 演示多态性
        System.out.println("\n4. 演示多态性:");
        demonstratePolymorphism();
    }
    
    private static void useConsoleLogger() {
        LoggerFactory factory = new ConsoleLoggerFactory();
        Logger logger = factory.createLogger();
        
        logger.info("应用程序启动成功");
        logger.warn("内存使用率较高");
        logger.error("数据库连接失败");
        logger.debug("调试信息: 变量值 = 100");
    }
    
    private static void useFileLogger() {
        LoggerFactory factory = new FileLoggerFactory("/var/log/myapp.log");
        Logger logger = factory.createLogger();
        
        logger.info("用户登录成功");
        logger.error("文件上传失败");
        
        // 使用默认方法
        factory.logInfo("通过工厂默认方法记录日志");
    }
    
    private static void useDatabaseLogger() {
        LoggerFactory factory = new DatabaseLoggerFactory("jdbc:mysql://localhost:3306/logs");
        Logger logger = factory.createLogger();
        
        logger.info("订单创建成功");
        logger.error("支付处理异常");
    }
    
    private static void demonstratePolymorphism() {
        // 多态性的体现:可以轻松切换不同的日志实现
        LoggerFactory[] factories = {
            new ConsoleLoggerFactory(),
            new FileLoggerFactory("/tmp/app.log"),
            new DatabaseLoggerFactory("jdbc:mysql://localhost:3306/app")
        };
        
        for (LoggerFactory factory : factories) {
            System.out.println("\n使用 " + factory.getClass().getSimpleName() + ":");
            factory.logInfo("这是一条测试日志信息");
            factory.logError("这是一个测试错误信息");
        }
    }
}

6. 进阶示例:配置化的日志工厂

java 复制代码
/**
 * 配置化日志工厂 - 根据配置创建不同的日志记录器
 */
public class ConfigurableLoggerFactory implements LoggerFactory {
    private LoggerConfig config;
    
    public ConfigurableLoggerFactory(LoggerConfig config) {
        this.config = config;
    }
    
    @Override
    public Logger createLogger() {
        switch (config.getType()) {
            case CONSOLE:
                return new ConsoleLogger();
            case FILE:
                return new FileLogger(config.getFilePath());
            case DATABASE:
                return new DatabaseLogger(config.getDataSource());
            default:
                throw new IllegalArgumentException("不支持的日志类型: " + config.getType());
        }
    }
}

/**
 * 日志配置类
 */
public class LoggerConfig {
    private LoggerType type;
    private String filePath;
    private String dataSource;
    
    public LoggerConfig(LoggerType type, String filePath, String dataSource) {
        this.type = type;
        this.filePath = filePath;
        this.dataSource = dataSource;
    }
    
    // Getter 方法
    public LoggerType getType() { return type; }
    public String getFilePath() { return filePath; }
    public String getDataSource() { return dataSource; }
}

/**
 * 日志类型枚举
 */
public enum LoggerType {
    CONSOLE, FILE, DATABASE
}

工厂方法模式的优点

1. 符合开闭原则

java 复制代码
// 添加新的日志类型时,不需要修改现有代码
public class CloudLogger implements Logger {
    // 实现云日志记录...
}

public class CloudLoggerFactory implements LoggerFactory {
    @Override
    public Logger createLogger() {
        return new CloudLogger(); // 新增工厂,不修改现有代码
    }
}

2. 客户端与具体产品解耦

java 复制代码
public class BusinessService {
    private LoggerFactory loggerFactory;
    
    public BusinessService(LoggerFactory loggerFactory) {
        this.loggerFactory = loggerFactory; // 依赖抽象,不依赖具体实现
    }
    
    public void processBusiness() {
        Logger logger = loggerFactory.createLogger();
        logger.info("业务处理开始");
        // 业务逻辑...
        logger.info("业务处理完成");
    }
}

3. 提高代码的可测试性

java 复制代码
// 测试时可以使用Mock工厂
public class TestLoggerFactory implements LoggerFactory {
    @Override
    public Logger createLogger() {
        return new MockLogger(); // 返回测试用的Mock对象
    }
}

工厂方法模式的缺点

1. 类的数量增加

每个具体产品都需要对应一个具体工厂类,会导致系统中类的数量成对增加。

2. 增加了系统的复杂性

对于简单对象的创建,使用工厂方法模式可能会显得过于复杂。

适用场景

  1. 无法预知对象的确切类型:运行时才能确定要创建的对象
  2. 希望扩展产品类型:需要添加新产品时不影响现有代码
  3. 需要解耦客户端和具体产品:客户端只关心产品接口
  4. 需要为不同的上下文提供不同的产品实现

与简单工厂模式的对比

特性 简单工厂模式 工厂方法模式
创建逻辑 集中在单个工厂类 分散在多个具体工厂类
开闭原则 违反(修改需要改动工厂类) 符合(扩展新工厂即可)
复杂度 相对简单 相对复杂
灵活性 较低 较高
适用场景 产品类型固定,变化较少 产品类型可能扩展

最佳实践

1. 使用依赖注入

java 复制代码
public class ApplicationContext {
    private static Map<String, LoggerFactory> factories = new HashMap<>();
    
    static {
        // 注册所有工厂
        factories.put("console", new ConsoleLoggerFactory());
        factories.put("file", new FileLoggerFactory("/app.log"));
    }
    
    public static LoggerFactory getLoggerFactory(String type) {
        return factories.get(type);
    }
}

2. 结合配置文件

java 复制代码
// 通过配置文件决定使用哪个工厂
Properties config = loadConfig();
String factoryType = config.getProperty("logger.factory");
LoggerFactory factory = ApplicationContext.getLoggerFactory(factoryType);

3. 使用泛型增强类型安全

java 复制代码
public interface LoggerFactory<T extends Logger> {
    T createLogger();
}

public class FileLoggerFactory implements LoggerFactory<FileLogger> {
    @Override
    public FileLogger createLogger() {
        return new FileLogger("/app.log");
    }
}

总结

工厂方法模式通过将对象的创建延迟到子类,完美解决了简单工厂模式违反开闭原则的问题。它提供了一种灵活的扩展机制,使得系统能够轻松应对变化。

核心价值:

  • 真正实现了面向对象设计的"开闭原则"
  • 提供了优秀的扩展性和维护性
  • 实现了创建逻辑与使用逻辑的彻底分离
  • 为框架设计和组件化提供了坚实基础

掌握工厂方法模式,能够帮助我们在面对复杂对象创建场景时,设计出更加灵活、可维护的系统架构。

相关推荐
Deschen3 小时前
设计模式-抽象工厂模式
java·设计模式·抽象工厂模式
粘豆煮包5 小时前
系统设计 System Design -4-2-系统设计问题-设计类似 TinyURL 的 URL 缩短服务 (改进版)
设计模式·架构
top_designer6 小时前
告别“静态”VI手册:InDesign与AE打造可交互的动态品牌规范
设计模式·pdf·交互·vi·工作流·after effects·indesign
非凡的世界7 小时前
深入理解 PHP 框架里的设计模式
开发语言·设计模式·php
一叶飘零_sweeeet7 小时前
深入 Spring 内核:解密 15 种设计模式的实战应用与底层实现
java·spring·设计模式
Mr_WangAndy8 小时前
C++设计模式_行为型模式_状态模式State
c++·设计模式·状态模式
bkspiderx9 小时前
C++设计模式之行为型模式:访问者模式(Visitor)
c++·设计模式·访问者模式
Lei活在当下9 小时前
【业务场景架构实战】8. 订单状态流转在 UI 端的呈现设计
android·设计模式·架构
Query*9 小时前
Java 设计模式——代理模式:从静态代理到 Spring AOP 最优实现
java·设计模式·代理模式