工厂方法模式(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: 2002.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}");
}
}
}工厂方法扩展:只需新增 ConcreteProductC 和 ConcreteCreatorC,无需修改已有代码。
四、适用场景
| 场景 | 说明 |
|---|---|
| 对象创建过程复杂 | 封装创建逻辑 |
| 需要根据条件创建不同类型对象 | 子类决定实例化哪个类 |
| 解耦产品创建和使用 | 客户端无需知道具体产品类 |
| 框架设计 | 提供扩展点供使用者实现 |
| 跨平台组件创建 | 不同平台对应不同工厂 |
五、优缺点分析
| 优点 | 缺点 |
|---|---|
| 符合开闭原则,易于扩展 | 每个产品都需要一个工厂类 |
| 解耦产品创建和使用 | 类数量增加 |
| 符合单一职责原则 | 客户端需要选择具体工厂 |
| 支持新产品族扩展 | 可能过度设计简单场景 |
六、常见踩坑与问题排查
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());八、全文总结
工厂方法模式的核心是 将对象的创建延迟到子类,实现创建与使用的解耦。
核心要点:
- 通过抽象工厂定义创建接口,子类决定具体产品
- 符合开闭原则,新增产品无需修改已有代码
- 与简单工厂相比,扩展性更强但类更多
- 适用于产品类型多、可能扩展的场景
- 可与依赖注入、注册中心等模式结合使用
