# 工厂方法模式
# 概念
工厂方法模式( Factory Method Pattern )是一种创建型设计模式,它定义了一个创建对象的接口(工厂接口),但由子类决定要实例化的类(具体产品类)。该模式将对象的创建逻辑封装在子类中,使得客户端代码可以独立于对象的创建过程。
# 作用
解耦创建和使用:将对象的创建和使用分离,客户端代码不需要知道对象的创建细节,只需要通过工厂接口获取对象。
灵活扩展:新增日志记录方式时,只需添加新的日志记录器类和对应的工厂类,无需修改现有代码,符合开闭原则。
封装创建逻辑:将对象的创建逻辑封装在工厂类中,便于统一管理和维护,减少重复代码。
支持多态:通过工厂接口可以创建不同类型的对象,客户端代码可以基于多态进行操作,提高代码的可复用性和可维护性。
# 场景
多态对象创建:多种同类对象创建,支持灵活扩展。
框架扩展设计:框架预留接口,用户自定义实现。
动态产品切换:运行时按条件创建不同产品实例。
复杂构造封装:封装多步初始化或依赖创建逻辑。
# 举例
假设我们有一个日志记录系统,需要支持不同类型的日志记录方式,比如文件日志、数据库日志、控制台日志等。我们可以使用工厂方法模式来设计这个系统。
定义日志记录器接口:
package net.feixiang.creational.factoryMethod;
/**
* 日志接口
*/
public interface Logger {
/**
* 记录日志
*
* @param message 日志消息
*/
void log(String message);
}
实现具体日志记录器类:
package net.feixiang.creational.factoryMethod;
/**
* 控制台日志类
*/
public class ConsoleLogger implements Logger {
@Override
public void log(String message) {
// 将日志输出到控制台
System.out.println("控制台日志: " + message);
}
}
package net.feixiang.creational.factoryMethod;
/**
* 数据库日志类
*/
public class DatabaseLogger implements Logger {
@Override
public void log(String message) {
// 将日志写入数据库
System.out.println("数据库日志: " + message);
}
}
package net.feixiang.creational.factoryMethod;
/**
* 文件日志类
*/
public class FileLogger implements Logger {
@Override
public void log(String message) {
// 将日志写入文件
System.out.println("文件日志: " + message);
}
}
定义工厂接口:
package net.feixiang.creational.factoryMethod;
/**
* 日志工厂接口
*/
public interface LoggerFactory {
/**
* 创建日志对象
*
* @return 日志对象
*/
Logger createLogger();
}
实现具体工厂类:
package net.feixiang.creational.factoryMethod;
/**
* 控制台日志工厂类
*/
public class ConsoleLoggerFactory implements LoggerFactory {
@Override
public Logger createLogger() {
return new ConsoleLogger();
}
}
package net.feixiang.creational.factoryMethod;
/**
* 数据库日志工厂类
*/
public class DatabaseLoggerFactory implements LoggerFactory {
@Override
public Logger createLogger() {
return new DatabaseLogger();
}
}
package net.feixiang.creational.factoryMethod;
/**
* 文件日志工厂类
*/
public class FileLoggerFactory implements LoggerFactory {
@Override
public Logger createLogger() {
return new FileLogger();
}
}
运行示例:
package net.feixiang.creational.factoryMethod;
/**
* 工厂方法模式演示
*/
public class FactoryMethodDemo {
public static void main(String[] args) {
// 使用控制台日志工厂
LoggerFactory consoleLoggerFactory = new ConsoleLoggerFactory();
Logger consoleLogger = consoleLoggerFactory.createLogger();
consoleLogger.log("这是一个控制台日志消息。");
// 使用数据库日志工厂
LoggerFactory databaseLoggerFactory = new DatabaseLoggerFactory();
Logger databaseLogger = databaseLoggerFactory.createLogger();
databaseLogger.log("这是一个数据库日志消息。");
// 使用文件日志工厂
LoggerFactory fileLoggerFactory = new FileLoggerFactory();
Logger fileLogger = fileLoggerFactory.createLogger();
fileLogger.log("这是一个文件日志消息。");
}
}
控制台输出:
控制台日志: 这是一个控制台日志消息。
数据库日志: 这是一个数据库日志消息。
文件日志: 这是一个文件日志消息。
# 反例
假设我们有一个日志记录系统,需要支持不同类型的日志记录方式,比如文件日志、数据库日志、控制台日志等。如果不使用工厂方法模式,可能会出现以下问题:
定义非工厂方法模式类:
package net.feixiang.creational.factoryMethod.contrary;
/**
* 非工厂方法模式
*/
public class NonFactoryMethod {
public static void log(String type, String message) {
// 后续如有增加类型,这里的代码需修改才能扩展,违反开闭原则,耦合度也高
if ("console".equals(type)) {
// 控制台日志记录逻辑
System.out.println("控制台日志: " + message);
}else if ("database".equals(type)) {
// 数据库日志记录逻辑
System.out.println("数据库日志: " + message);
} else if ("file".equals(type)) {
// 文件日志记录逻辑
System.out.println("文件日志: " + message);
} else {
throw new IllegalArgumentException("无效日志类型: " + type);
}
}
}
运行示例:
package net.feixiang.creational.factoryMethod.contrary;
/**
* 非工厂方法模式演示
*/
public class NonFactoryMethodDemo {
public static void main(String[] args) {
NonFactoryMethod.log("console", "这是一个控制台日志消息。");
NonFactoryMethod.log("database", "这是一个数据库日志消息。");
NonFactoryMethod.log("file", "这是一个文件日志消息。");
}
}
控制台输出:
控制台日志: 这是一个控制台日志消息。
数据库日志: 这是一个数据库日志消息。
文件日志: 这是一个文件日志消息。
# 存在的短板
违反单一职责原则
Logger
类既负责日志记录逻辑,又负责根据类型创建不同的日志记录器,职责不单一。当需要新增日志记录类型时,需要修改Logger
类的代码,违反开闭原则。扩展性差
新增日志记录类型时,需要修改
Logger
类的log
方法,添加新的if-else
或switch-case
分支。这会导致Logger
类变得越来越臃肿,难以维护。客户端代码与日志记录器耦合度高
客户端代码需要知道所有日志记录器的类型,并在调用时传入正确的类型字符串。如果类型字符串拼写错误,可能会导致运行时错误。
难以统一管理日志记录器的创建
日志记录器的创建逻辑分散在客户端代码中,难以统一管理和维护。例如,如果需要对日志记录器的创建进行额外的配置或初始化操作,需要在多个地方修改代码。
不利于多态的使用
这种实现方式下,客户端代码无法基于多态来操作日志记录器,只能通过类型字符串来区分不同的日志记录方式,代码的灵活性和可复用性较低。
相比之下,使用工厂方法模式可以将日志记录器的创建逻辑封装在工厂类中,客户端代码通过工厂接口获取日志记录器对象,解耦了客户端代码与日志记录器的创建过程,提高了代码的可扩展性、可维护性和灵活性。
# 开闭原则
开闭原则( Open-Closed Principle, OCP )是软件设计中的一项核心原则,由伯特兰·梅耶( Bertrand Meyer )提出。该原则指出:软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。
# 核心思想
对扩展开放
软件实体应该能够通过扩展其功能来适应新的需求或变化,而无需修改其现有的代码。
对修改关闭
现有的代码在完成其功能后,应尽量避免被修改,以降低引入错误的风险并保持代码的稳定性。
# 举例
直接实例化对象:
package net.feixiang.creational.factoryMethod.contrary;
import net.feixiang.creational.factoryMethod.Logger;
import net.feixiang.creational.factoryMethod.ConsoleLogger;
import net.feixiang.creational.factoryMethod.DatabaseLogger;
import net.feixiang.creational.factoryMethod.FileLogger;
/**
* 不符合开闭原则的工厂方法模式示例:直接依赖具体类,增加新日志类型需要修改现有代码。
*/
public class NonOpenClosedPrincipleDemo {
public static void main(String[] args) {
// 直接依赖具体类
Logger logger;
String type = "console";
if ("console".equals(type)) {
// 实例化控制台日志类
logger = new ConsoleLogger();
} else if ("database".equals(type)) {
// 实例化数据库日志类
logger = new DatabaseLogger();
} else if ("file".equals(type)) {
// 实例化文件日志类
logger = new FileLogger();
} else {
throw new IllegalArgumentException("无效日志类型: " + type);
}
// 增加其它日志类型需要在这里继续用else if,耦合性高
}
}
# 解析
违反开闭原则:新增日志(如
Web
)时,必须修改客户端代码中的条件判断逻辑。高耦合:客户端直接依赖具体类,难以替换或扩展产品。
重复代码:若多个地方需要创建对象,相同的条件判断逻辑会重复出现。
总结
工厂方法模式是创建型设计模式,定义创建对象接口,由子类决定实例化类。它能解耦创建与使用,灵活扩展,封装创建逻辑,支持多态。适用于多态对象创建等场景,符合开闭原则,避免直接实例化对象的诸多短板。

微信公众号

QQ交流群
如若发现错误,诚心感谢反馈。
愿你倾心相念,愿你学有所成。
愿你朝华相顾,愿你前程似锦。