设计模式之:工厂方法模式
文章目录
- 什么是工厂方法模式?
- 核心思想
- 模式结构
- 完整示例:日志记录系统
- 1. 定义抽象产品
- 2. 实现具体产品
- 3. 定义抽象工厂
- 4. 实现具体工厂
- 5. 客户端使用示例
- 6. 进阶示例:配置化的日志工厂
- 工厂方法模式的优点
- 1. 符合开闭原则
- 2. 客户端与具体产品解耦
- 3. 提高代码的可测试性
- 工厂方法模式的缺点
- 1. 类的数量增加
- 2. 增加了系统的复杂性
- 适用场景
- 与简单工厂模式的对比
- 最佳实践
- 1. 使用依赖注入
- 2. 结合配置文件
- 3. 使用泛型增强类型安全
- 总结
什么是工厂方法模式?
工厂方法模式(Factory Method Pattern)是一种经典的创建型设计模式,它定义了一个创建对象的接口,但让子类决定要实例化哪一个类。工厂方法让类的实例化推迟到子类,完美遵循了“开闭原则”,实现了对象创建的可扩展性。
核心思想
工厂方法模式的核心是:定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类。工厂方法让类的实例化延迟到子类进行。
模式结构
工厂方法模式包含四个核心角色:
- 抽象产品(Product):定义产品的接口
- 具体产品(Concrete Product):实现抽象产品接口的具体类
- 抽象工厂(Creator):声明工厂方法,返回产品对象
- 具体工厂(Concrete Creator):重写工厂方法,返回具体产品实例
完整示例:日志记录系统
让我们通过一个完整的日志记录系统来深入理解工厂方法模式。
1. 定义抽象产品
/*** 日志记录器接口 - 抽象产品角色* 定义日志记录的基本操作*/
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. 实现具体产品
/*** 文件日志记录器 - 具体产品角色* 将日志记录到文件中*/
public class FileLogger implements Logger {private String filePath;public FileLogger(String filePath) {this.filePath = filePath;System.out.println("初始化文件日志记录器,文件路径: " + filePath);}@Overridepublic void info(String message) {String log = String.format("[INFO] %s %s", getTimestamp(), message);writeToFile(log);}@Overridepublic void error(String message) {String log = String.format("[ERROR] %s %s", getTimestamp(), message);writeToFile(log);}@Overridepublic void warn(String message) {String log = String.format("[WARN] %s %s", getTimestamp(), message);writeToFile(log);}@Overridepublic 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 {@Overridepublic void info(String message) {System.out.println("\u001B[32m[INFO] " + getTimestamp() + " " + message + "\u001B[0m");}@Overridepublic void error(String message) {System.out.println("\u001B[31m[ERROR] " + getTimestamp() + " " + message + "\u001B[0m");}@Overridepublic void warn(String message) {System.out.println("\u001B[33m[WARN] " + getTimestamp() + " " + message + "\u001B[0m");}@Overridepublic 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);}@Overridepublic void info(String message) {String log = String.format("INSERT INTO logs (level, message, timestamp) VALUES ('INFO', '%s', '%s')", message, getTimestamp());executeSQL(log);}@Overridepublic void error(String message) {String log = String.format("INSERT INTO logs (level, message, timestamp) VALUES ('ERROR', '%s', '%s')", message, getTimestamp());executeSQL(log);}@Overridepublic void warn(String message) {String log = String.format("INSERT INTO logs (level, message, timestamp) VALUES ('WARN', '%s', '%s')", message, getTimestamp());executeSQL(log);}@Overridepublic 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) {// 模拟执行SQLSystem.out.println("执行SQL: " + sql);}private String getTimestamp() {return java.time.LocalDateTime.now().toString();}
}
3. 定义抽象工厂
/*** 日志记录器工厂接口 - 抽象工厂角色* 声明工厂方法,由子类实现具体创建逻辑*/
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. 实现具体工厂
/*** 文件日志记录器工厂 - 具体工厂角色* 负责创建文件日志记录器*/
public class FileLoggerFactory implements LoggerFactory {private String filePath;public FileLoggerFactory(String filePath) {this.filePath = filePath;}@Overridepublic Logger createLogger() {return new FileLogger(filePath);}
}/*** 控制台日志记录器工厂 - 具体工厂角色* 负责创建控制台日志记录器*/
public class ConsoleLoggerFactory implements LoggerFactory {@Overridepublic Logger createLogger() {return new ConsoleLogger();}
}/*** 数据库日志记录器工厂 - 具体工厂角色* 负责创建数据库日志记录器*/
public class DatabaseLoggerFactory implements LoggerFactory {private String dataSource;public DatabaseLoggerFactory(String dataSource) {this.dataSource = dataSource;}@Overridepublic Logger createLogger() {return new DatabaseLogger(dataSource);}
}
5. 客户端使用示例
/*** 应用程序类 - 客户端代码* 演示工厂方法模式的使用*/
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. 进阶示例:配置化的日志工厂
/*** 配置化日志工厂 - 根据配置创建不同的日志记录器*/
public class ConfigurableLoggerFactory implements LoggerFactory {private LoggerConfig config;public ConfigurableLoggerFactory(LoggerConfig config) {this.config = config;}@Overridepublic 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. 符合开闭原则
// 添加新的日志类型时,不需要修改现有代码
public class CloudLogger implements Logger {// 实现云日志记录...
}public class CloudLoggerFactory implements LoggerFactory {@Overridepublic Logger createLogger() {return new CloudLogger(); // 新增工厂,不修改现有代码}
}
2. 客户端与具体产品解耦
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. 提高代码的可测试性
// 测试时可以使用Mock工厂
public class TestLoggerFactory implements LoggerFactory {@Overridepublic Logger createLogger() {return new MockLogger(); // 返回测试用的Mock对象}
}
工厂方法模式的缺点
1. 类的数量增加
每个具体产品都需要对应一个具体工厂类,会导致系统中类的数量成对增加。
2. 增加了系统的复杂性
对于简单对象的创建,使用工厂方法模式可能会显得过于复杂。
适用场景
- 无法预知对象的确切类型:运行时才能确定要创建的对象
- 希望扩展产品类型:需要添加新产品时不影响现有代码
- 需要解耦客户端和具体产品:客户端只关心产品接口
- 需要为不同的上下文提供不同的产品实现
与简单工厂模式的对比
特性 | 简单工厂模式 | 工厂方法模式 |
---|---|---|
创建逻辑 | 集中在单个工厂类 | 分散在多个具体工厂类 |
开闭原则 | 违反(修改需要改动工厂类) | 符合(扩展新工厂即可) |
复杂度 | 相对简单 | 相对复杂 |
灵活性 | 较低 | 较高 |
适用场景 | 产品类型固定,变化较少 | 产品类型可能扩展 |
最佳实践
1. 使用依赖注入
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. 结合配置文件
// 通过配置文件决定使用哪个工厂
Properties config = loadConfig();
String factoryType = config.getProperty("logger.factory");
LoggerFactory factory = ApplicationContext.getLoggerFactory(factoryType);
3. 使用泛型增强类型安全
public interface LoggerFactory<T extends Logger> {T createLogger();
}public class FileLoggerFactory implements LoggerFactory<FileLogger> {@Overridepublic FileLogger createLogger() {return new FileLogger("/app.log");}
}
总结
工厂方法模式通过将对象的创建延迟到子类,完美解决了简单工厂模式违反开闭原则的问题。它提供了一种灵活的扩展机制,使得系统能够轻松应对变化。
核心价值:
- 真正实现了面向对象设计的"开闭原则"
- 提供了优秀的扩展性和维护性
- 实现了创建逻辑与使用逻辑的彻底分离
- 为框架设计和组件化提供了坚实基础
掌握工厂方法模式,能够帮助我们在面对复杂对象创建场景时,设计出更加灵活、可维护的系统架构。