Skip to content

工厂方法模式(Factory Method)

前言

工厂方法模式是一种常用的创建型设计模式,它通过定义一个创建对象的接口,让子类决定实例化哪个类,从而将对象的创建和使用解耦。本文将详细讲解工厂方法模式的核心原理、实现方式、与简单工厂的区别及实际应用。


一、核心概念

1.1 定义

定义一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法使一个类的实例化延迟到其子类。

1.2 核心角色

角色说明
抽象产品(Product)定义产品的接口
具体产品(ConcreteProduct)实现产品接口的具体类
抽象工厂(Creator)声明工厂方法,返回产品对象
具体工厂(ConcreteCreator)实现工厂方法,返回具体产品实例

二、代码实现

2.1 基础实现

php
<?php
// 抽象产品
interface Product
{
    public function getName(): string;
    public function getPrice(): float;
    public function use(): string;
}

// 具体产品 A
class ConcreteProductA implements Product
{
    public function getName(): string { return 'Product A'; }
    public function getPrice(): float { return 100; }

    public function use(): string
    {
        return "Using {$this->getName()}, price: {$this->getPrice()}";
    }
}

// 具体产品 B
class ConcreteProductB implements Product
{
    public function getName(): string { return 'Product B'; }
    public function getPrice(): float { return 200; }

    public function use(): string
    {
        return "Using {$this->getName()}, price: {$this->getPrice()}";
    }
}

// 抽象工厂
abstract class Creator
{
    // 工厂方法,由子类实现
    abstract public function factoryMethod(): Product;

    // 业务逻辑,使用工厂方法创建产品
    public function operation(): string
    {
        $product = $this->factoryMethod();
        return "Creator: Created " . $product->use();
    }
}

// 具体工厂 A
class ConcreteCreatorA extends Creator
{
    public function factoryMethod(): Product
    {
        return new ConcreteProductA();
    }
}

// 具体工厂 B
class ConcreteCreatorB extends Creator
{
    public function factoryMethod(): Product
    {
        return new ConcreteProductB();
    }
}

// 使用
$creatorA = new ConcreteCreatorA();
echo $creatorA->operation() . PHP_EOL;
// Creator: Created Using Product A, price: 100

$creatorB = new ConcreteCreatorB();
echo $creatorB->operation() . PHP_EOL;
// Creator: Created Using Product B, price: 200

2.2 实际案例:日志记录器

php
<?php
// 日志记录器接口
interface Logger
{
    public function log(string $message): void;
}

// 文件日志记录器
class FileLogger implements Logger
{
    public function log(string $message): void
    {
        echo "[File] {$message}" . PHP_EOL;
    }
}

// 控制台日志记录器
class ConsoleLogger implements Logger
{
    public function log(string $message): void
    {
        echo "[Console] {$message}" . PHP_EOL;
    }
}

// 数据库日志记录器
class DatabaseLogger implements Logger
{
    public function log(string $message): void
    {
        echo "[Database] {$message}" . PHP_EOL;
    }
}

// 抽象日志工厂
abstract class LoggerFactory
{
    abstract public function createLogger(): Logger;

    public function log(string $message): void
    {
        $logger = $this->createLogger();
        $logger->log($message);
    }
}

// 文件日志工厂
class FileLoggerFactory extends LoggerFactory
{
    public function createLogger(): Logger
    {
        return new FileLogger();
    }
}

// 控制台日志工厂
class ConsoleLoggerFactory extends LoggerFactory
{
    public function createLogger(): Logger
    {
        return new ConsoleLogger();
    }
}

// 使用
$fileFactory = new FileLoggerFactory();
$fileFactory->log('Application started');
// [File] Application started

$consoleFactory = new ConsoleLoggerFactory();
$consoleFactory->log('User logged in');
// [Console] User logged in

三、与简单工厂的区别

对比项简单工厂工厂方法
结构一个工厂类,一个静态方法抽象工厂 + 具体工厂子类
扩展需修改工厂类代码新增工厂子类即可
开闭原则不符合符合
复杂度较高
适用场景产品少且稳定产品多且可能扩展

简单工厂示例

php
<?php
// 简单工厂(不符合开闭原则)
class SimpleFactory
{
    public static function createProduct(string $type): Product
    {
        switch ($type) {
            case 'A':
                return new ConcreteProductA();
            case 'B':
                return new ConcreteProductB();
            default:
                throw new InvalidArgumentException("Unknown product type: {$type}");
        }
    }
}

// 新增产品 C 时需要修改工厂类
class SimpleFactory
{
    public static function createProduct(string $type): Product
    {
        switch ($type) {
            case 'A':
                return new ConcreteProductA();
            case 'B':
                return new ConcreteProductB();
            case 'C':
                return new ConcreteProductC(); // 修改了原有代码
            default:
                throw new InvalidArgumentException("Unknown type: {$type}");
        }
    }
}

工厂方法扩展:只需新增 ConcreteProductCConcreteCreatorC,无需修改已有代码。


四、适用场景

场景说明
对象创建过程复杂封装创建逻辑
需要根据条件创建不同类型对象子类决定实例化哪个类
解耦产品创建和使用客户端无需知道具体产品类
框架设计提供扩展点供使用者实现
跨平台组件创建不同平台对应不同工厂

五、优缺点分析

优点缺点
符合开闭原则,易于扩展每个产品都需要一个工厂类
解耦产品创建和使用类数量增加
符合单一职责原则客户端需要选择具体工厂
支持新产品族扩展可能过度设计简单场景

六、常见踩坑与问题排查

6.1 工厂方法返回 null

php
<?php
// 错误:工厂方法可能返回 null
class BadFactory extends Creator
{
    public function factoryMethod(): ?Product
    {
        if ($this->someCondition) {
            return new ConcreteProductA();
        }
        return null; // 问题:返回 null
    }
}

// 正确:使用默认产品或抛出异常
class GoodFactory extends Creator
{
    public function factoryMethod(): Product
    {
        if ($this->someCondition) {
            return new ConcreteProductA();
        }
        return new DefaultProduct(); // 默认产品
    }
}

6.2 工厂方法与构造函数混用

避免在工厂方法中直接调用构造函数创建依赖对象,应通过依赖注入传递。

6.3 过度使用

当产品类型少且稳定时,简单工厂即可,不必强行使用工厂方法。


七、优化方案与进阶

7.1 参数化工厂方法

php
<?php
abstract class Creator
{
    abstract public function factoryMethod(string $type): Product;

    public function create(string $type): Product
    {
        return $this->factoryMethod($type);
    }
}

class UniversalCreator extends Creator
{
    public function factoryMethod(string $type): Product
    {
        switch ($type) {
            case 'A':
                return new ConcreteProductA();
            case 'B':
                return new ConcreteProductB();
            default:
                throw new InvalidArgumentException("Unknown type: {$type}");
        }
    }
}

7.2 结合依赖注入

php
<?php
// 工厂注册中心
class FactoryRegistry
{
    private array $factories = [];

    public function register(string $type, callable $factory): void
    {
        $this->factories[$type] = $factory;
    }

    public function create(string $type): Product
    {
        if (!isset($this->factories[$type])) {
            throw new InvalidArgumentException("Unknown type: {$type}");
        }
        return ($this->factories[$type])();
    }
}

$registry = new FactoryRegistry();
$registry->register('A', fn() => new ConcreteProductA());
$registry->register('B', fn() => new ConcreteProductB());

八、全文总结

工厂方法模式的核心是 将对象的创建延迟到子类,实现创建与使用的解耦

核心要点

  1. 通过抽象工厂定义创建接口,子类决定具体产品
  2. 符合开闭原则,新增产品无需修改已有代码
  3. 与简单工厂相比,扩展性更强但类更多
  4. 适用于产品类型多、可能扩展的场景
  5. 可与依赖注入、注册中心等模式结合使用