Java工厂方法案例如何编写

wen java案例 32

Java工厂方法案例如何编写:从基础到高级的完整指南

📑 目录导读

  1. 工厂方法模式是什么?
  2. 为什么要用工厂方法?
  3. 基础案例:日志记录器
  4. 进阶案例:数据库连接工厂
  5. 工厂方法 VS 简单工厂
  6. 常见误区与最佳实践
  7. 问答环节

工厂方法模式是什么?

工厂方法(Factory Method)是Java中最常用的创建型设计模式之一,它定义一个创建对象的接口,但让子类决定实例化哪个类,简单说就是:把对象的创建延迟到子类中

Java工厂方法案例如何编写

很多开发者会问:“直接new对象不就好了,为什么要这么复杂?”
核心原因:当创建对象的过程需要根据不同条件变化,或者对象创建逻辑复杂时,工厂方法能让你不修改现有代码就能扩展新的产品类型,符合开闭原则


为什么要用工厂方法?

❌ 不用工厂的痛点

// 客户端直接依赖具体类
if (type.equals("file")) {
    Logger logger = new FileLogger();
} else if (type.equals("console")) {
    Logger logger = new ConsoleLogger();
}
  • 每新增一种日志类型,就要修改这段if-else代码
  • 违反开闭原则,容易出错

✅ 使用工厂方法的好处

  1. 解耦:客户端只依赖抽象接口,不依赖具体实现
  2. 扩展性:新增产品类无需修改已有代码
  3. 复用性:创建逻辑集中在工厂子类中
  4. 遵守单一职责:每个类只负责自己的创建逻辑

基础案例:日志记录器

1 场景描述

假设我们正在开发一个应用,需要支持多种日志记录方式:文件日志、控制台日志、数据库日志,未来可能还会增加网络日志。

2 实现步骤

第一步:定义产品接口

public interface Logger {
    void log(String message);
}

第二步:实现具体产品

public class FileLogger implements Logger {
    @Override
    public void log(String message) {
        System.out.println("写入文件: " + message);
        // 实际的文件写入逻辑
    }
}
public class ConsoleLogger implements Logger {
    @Override
    public void log(String message) {
        System.out.println("控制台输出: " + message);
    }
}

第三步:定义工厂接口(核心)

public abstract class LoggerFactory {
    public abstract Logger createLogger();
    // 可选:公共业务方法
    public void logMessage(String message) {
        Logger logger = createLogger();
        logger.log(message);
    }
}

第四步:实现具体工厂

public class FileLoggerFactory extends LoggerFactory {
    @Override
    public Logger createLogger() {
        // 这里可以做复杂的初始化配置
        return new FileLogger();
    }
}
public class ConsoleLoggerFactory extends LoggerFactory {
    @Override
    public Logger createLogger() {
        return new ConsoleLogger();
    }
}

第五步:客户端使用

public class Client {
    public static void main(String[] args) {
        LoggerFactory factory = new FileLoggerFactory();
        Logger logger = factory.createLogger();
        logger.log("这是文件日志");
        // 切换只需改一行
        factory = new ConsoleLoggerFactory();
        factory.logMessage("这是控制台日志");
    }
}

3 扩展新类型

现在需要增加数据库日志,只需:

  1. 创建 DatabaseLogger implements Logger
  2. 创建 DatabaseLoggerFactory extends LoggerFactory
  3. 客户端直接使用新工厂即可,无需修改任何已有代码

进阶案例:数据库连接工厂

1 场景描述

开发一个支持多种数据库的中间件:MySQL、PostgreSQL、Oracle,每个数据库的连接参数和驱动加载都不同。

2 更复杂的实现

产品接口

public interface DatabaseConnection {
    void connect();
    void executeQuery(String sql);
    void disconnect();
}

工厂接口(带参数)

public abstract class DatabaseFactory {
    public abstract DatabaseConnection createConnection(String url, String username, String password);
    // 模板方法模式结合工厂方法
    public DatabaseConnection createAndConnect(String url, String username, String password) {
        DatabaseConnection conn = createConnection(url, username, password);
        conn.connect();
        return conn;
    }
}

具体工厂

public class MySQLFactory extends DatabaseFactory {
    @Override
    public DatabaseConnection createConnection(String url, String username, String password) {
        // 验证参数
        if (url == null || url.isEmpty()) {
            throw new IllegalArgumentException("URL不能为空");
        }
        // 配置MySQL特有参数
        Properties props = new Properties();
        props.setProperty("useSSL", "false");
        props.setProperty("characterEncoding", "UTF-8");
        return new MySQLConnection(url, username, password, props);
    }
}

客户端通过配置文件选择工厂

public class DatabaseManager {
    private DatabaseFactory factory;
    public DatabaseManager(String dbType) {
        // 通过反射或配置动态加载工厂
        if ("mysql".equalsIgnoreCase(dbType)) {
            factory = new MySQLFactory();
        } else if ("postgresql".equalsIgnoreCase(dbType)) {
            factory = new PostgreSQLFactory();
        } else {
            throw new IllegalArgumentException("不支持的数据库类型");
        }
    }
    public void execute(String url, String user, String pass, String sql) {
        DatabaseConnection conn = factory.createAndConnect(url, user, pass);
        conn.executeQuery(sql);
        conn.disconnect();
    }
}

工厂方法 VS 简单工厂

对比维度 简单工厂 工厂方法
创建方式 一个工厂类负责创建所有产品 每个产品对应一个工厂子类
扩展性 新增产品需修改工厂类 新增产品只需增加新子类
复杂度 简单,但违背开闭原则 稍复杂,完全符合开闭原则
适用场景 产品种类少且固定 产品种类多且经常扩展

一个真实的例子

  • 简单工厂:就像一家餐馆的“主厨”决定今天做什么菜,所有菜都他做
  • 工厂方法:就像连锁店,每家分店有自己的主厨(子类),但都遵循总部的配方标准(抽象工厂)

常见误区与最佳实践

❌ 常见错误

  1. 滥用工厂方法:当产品类型固定不变时,简单工厂更合适
  2. 工厂类过于复杂:把业务逻辑也塞进工厂里
  3. 忘记使用抽象:客户端直接引用具体工厂类,没有面向接口编程

✅ 最佳实践

  1. 工厂命名规范XxxFactoryXxxBuilder(如果涉及构建过程)
  2. 结合配置使用:从配置文件读取工厂类名,通过反射实例化
  3. 防止工厂泛滥:当一个工厂只创建一个产品时,考虑使用泛型
  4. 与DI容器结合:Spring的FactoryBean接口就是工厂方法的优秀实现

⚡ 性能考虑

  • 如果创建对象频繁,且对象轻量,考虑对象池+工厂方法
  • 工厂方法会增加类数量,但JVM对少量类的加载开销可以忽略

问答环节

Q1: 工厂方法是不是每次都要创建一个新工厂?

不一定,工厂实例可以复用,特别是当工厂是无状态时,可以将工厂设为单例或使用池化技术。

Q2: 工厂方法和抽象工厂有什么区别?

关键区别

  • 工厂方法:生产单一产品,通过继承扩展
  • 抽象工厂:生产产品族(多个相关产品),通过组合扩展

Q3: 可以用Java 8的Lambda简化工厂方法吗?

可以,对于简单的工厂逻辑:

Map<String, Supplier<Logger>> factoryMap = new HashMap<>();
factoryMap.put("file", FileLogger::new);
factoryMap.put("console", ConsoleLogger::new);
Logger logger = factoryMap.get(type).get();

但复杂初始化逻辑仍需要专门的工厂类。

Q4: 工厂方法和策略模式很像,怎么区分?

本质区别

  • 工厂方法:关注对象创建
  • 策略模式:关注算法行为

Q5: 有没有开源框架使用了工厂方法?

非常多

  • Spring的BeanFactoryFactoryBean
  • JDBC的DriverManager.getConnection()(虽然更多是简单工厂风格)
  • Log4j2的LoggerFactory

工厂方法模式是Java开发者必须掌握的设计模式之一,它通过将对象的创建过程封装到子类中,让代码更加灵活和可扩展,在实际项目中,我们通常不会只用纯工厂方法,而是会结合模板方法策略模式反射技术来构建更强大的创建机制。

设计模式是工具,不是教条,当你的代码中出现重复的“new”操作,或者需要频繁修改创建逻辑时,就是引入工厂方法的最佳时机。

建议你在自己的练习项目中有意识地使用工厂方法重构现有的“硬编码”创建逻辑,体会它带来的灵活性和可维护性的提升。

抱歉,评论功能暂时关闭!