Skip to content

工厂方法模式

引言

在面向对象的软件开发中,我们经常面临对象创建的问题。如果直接在代码中使用 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();
    }
}

优点

  • 符合开闭原则:增加新的产品类时只需扩展工厂类,无需修改已有代码。
  • 解耦:客户端代码与具体产品类解耦,只需通过工厂接口操作产品。
  • 可扩展性强:支持多种产品变体,适用于多态场景。

缺点

  • 类数量增加:每增加一个产品都需要增加一个对应的工厂类,增加了系统的复杂度。
  • 实现门槛略高:需要理解接口、抽象类、继承等面向对象的基本概念。

扩展

  • 与配置文件结合:通过配置文件动态决定使用哪个工厂,进一步提高灵活性。
  • 与反射结合:利用反射机制动态加载类,减少工厂类的数量。

模式协作

工厂方法模式常与以下模式协作:

  • 抽象工厂模式:当需要创建一组相关或依赖对象的家族时,可以使用抽象工厂模式。
  • 模板方法模式:在工厂方法的实现中,可以使用模板方法定义创建对象的步骤。
  • 单例模式:工厂本身可以是单例,确保全局只有一个工厂实例。

延伸思考

  • 与依赖注入的关系:工厂方法模式是实现依赖注入的一种方式,尤其是在框架设计中。
  • 与策略模式的结合:可以通过策略模式动态选择使用哪个工厂,实现更灵活的对象创建逻辑。

总结

工厂方法模式是一种非常实用的设计模式,它通过将对象的创建过程封装在工厂类中,实现了对客户端代码的解耦,提高了系统的可扩展性和可维护性。虽然它会增加类的数量,但在大型项目或框架设计中,这种代价是值得的。掌握工厂方法模式对于理解面向对象设计原则和构建灵活、可扩展的系统具有重要意义。