工厂方法模式
引言
在面向对象的软件开发中,我们经常面临对象创建的问题。如果直接在代码中使用 new 关键字来创建对象,会导致代码耦合度高、难以扩展和维护。为了解决这些问题,设计模式中引入了工厂方法模式(Factory Method Pattern),它是一种创建型设计模式,用于将对象的创建过程封装起来,使得客户端代码与具体类解耦。
诞生背景
工厂方法模式的诞生源于对“开闭原则”(Open-Closed Principle)的追求。在早期的面向对象系统中,对象的创建通常硬编码在客户端代码中,一旦需要新增一个产品类型,就需要修改客户端代码,这违背了“对扩展开放,对修改关闭”的原则。工厂方法模式通过引入一个工厂接口,使得新增产品类型时只需扩展而无需修改已有代码。
演进过程
- 早期做法:直接使用
new创建对象,耦合度高。 - 简单工厂:引入一个工厂类,根据参数创建不同的产品,但违反了开闭原则。
- 工厂方法模式:将工厂类抽象化,由子类决定具体创建哪个产品,实现了开闭原则。
核心概念
工厂方法模式的核心是定义一个用于创建对象的接口,但让子类决定实例化哪一个类。工厂方法模式将对象的创建延迟到子类。
Product:定义产品的公共接口。ConcreteProduct:实现Product接口的具体产品类。Factory:声明工厂方法,返回一个Product对象。ConcreteFactory:实现工厂方法,返回一个具体的ConcreteProduct实例。
通用实现
java实现
java
// 抽象产品
public interface Product {
void use();
}
// 具体产品A
public class ConcreteProductA implements Product {
@Override
public void use() {
System.out.println("Using Product A");
}
}
// 具体产品B
public class ConcreteProductB implements Product {
@Override
public void use() {
System.out.println("Using Product B");
}
}
// 抽象工厂
public interface Factory {
Product createProduct();
}
// 具体工厂A
public class ConcreteFactoryA implements Factory {
@Override
public Product createProduct() {
return new ConcreteProductA();
}
}
// 具体工厂B
public class ConcreteFactoryB implements Factory {
@Override
public Product createProduct() {
return new ConcreteProductB();
}
}php实现
php
// 抽象产品
interface Product {
public function use();
}
// 具体产品A
class ConcreteProductA implements Product {
public function use() {
echo "Using Product A\n";
}
}
// 具体产品B
class ConcreteProductB implements Product {
public function use() {
echo "Using Product B\n";
}
}
// 抽象工厂
interface Factory {
public function createProduct(): Product;
}
// 具体工厂A
class ConcreteFactoryA implements Factory {
public function createProduct(): Product {
return new ConcreteProductA();
}
}
// 具体工厂B
class ConcreteFactoryB implements Factory {
public function createProduct(): Product {
return new ConcreteProductB();
}
}应用场景
- 当一个类不知道它所必须创建的对象的类时。
- 当一个类希望由它的子类来指定它所创建的对象时。
- 当一个类将创建对象的职责委托给多个帮助子类中的某一个,并且希望将哪一个帮助子类被使用这一知识局部化时。
案例
以日志记录器为例,系统需要支持多种日志记录方式(如文件日志、数据库日志等)。
java实现
php
// 抽象产品
public interface Logger {
void log(String message);
}
// 具体产品 - 文件日志
public class FileLogger implements Logger {
@Override
public void log(String message) {
System.out.println("File Log: " + message);
}
}
// 具体产品 - 数据库日志
public class DatabaseLogger implements Logger {
@Override
public void log(String message) {
System.out.println("Database Log: " + 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();
}
}php实现
php
// 抽象产品
interface Logger {
public function log(string $message);
}
// 具体产品 - 文件日志
class FileLogger implements Logger {
public function log(string $message) {
echo "File Log: $message\n";
}
}
// 具体产品 - 数据库日志
class DatabaseLogger implements Logger {
public function log(string $message) {
echo "Database Log: $message\n";
}
}
// 抽象工厂
interface LoggerFactory {
public function createLogger(): Logger;
}
// 具体工厂 - 文件日志工厂
class FileLoggerFactory implements LoggerFactory {
public function createLogger(): Logger {
return new FileLogger();
}
}
// 具体工厂 - 数据库日志工厂
class DatabaseLoggerFactory implements LoggerFactory {
public function createLogger(): Logger {
return new DatabaseLogger();
}
}优点
- 符合开闭原则:增加新的产品类时只需扩展工厂类,无需修改已有代码。
- 解耦:客户端代码与具体产品类解耦,只需通过工厂接口操作产品。
- 可扩展性强:支持多种产品变体,适用于多态场景。
缺点
- 类数量增加:每增加一个产品都需要增加一个对应的工厂类,增加了系统的复杂度。
- 实现门槛略高:需要理解接口、抽象类、继承等面向对象的基本概念。
扩展
- 与配置文件结合:通过配置文件动态决定使用哪个工厂,进一步提高灵活性。
- 与反射结合:利用反射机制动态加载类,减少工厂类的数量。
模式协作
工厂方法模式常与以下模式协作:
- 抽象工厂模式:当需要创建一组相关或依赖对象的家族时,可以使用抽象工厂模式。
- 模板方法模式:在工厂方法的实现中,可以使用模板方法定义创建对象的步骤。
- 单例模式:工厂本身可以是单例,确保全局只有一个工厂实例。
延伸思考
- 与依赖注入的关系:工厂方法模式是实现依赖注入的一种方式,尤其是在框架设计中。
- 与策略模式的结合:可以通过策略模式动态选择使用哪个工厂,实现更灵活的对象创建逻辑。
总结
工厂方法模式是一种非常实用的设计模式,它通过将对象的创建过程封装在工厂类中,实现了对客户端代码的解耦,提高了系统的可扩展性和可维护性。虽然它会增加类的数量,但在大型项目或框架设计中,这种代价是值得的。掌握工厂方法模式对于理解面向对象设计原则和构建灵活、可扩展的系统具有重要意义。