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