真会工厂模式是动手写出可扩展、不耦合、新增类型无需修改旧代码的工厂;php中构造函数不能返回对象,必须用静态或实例方法(如create)封装new,工厂类应无业务状态,配置宜用数组而非switch。

别从“工厂模式是什么”开始学,直接动手写一个能跑通、不耦合、加新类型不用改老代码的工厂——这才是真会了。
为什么不能在__construct里返回对象
这是新手最常踩的坑:写个BookFactory类,在__construct()里return new Book(...),结果new BookFactory()拿到的还是BookFactory实例,不是Book。因为PHP的new操作符强制返回自身类实例,构造函数里的return会被忽略。
正确做法只有一条:用静态方法或实例方法封装new,比如CarFactory::create('sports'),而不是new CarFactory('sports')。
- 所有工厂逻辑必须落在可调用的方法里,不能塞进构造函数
- 工厂类本身不该有业务状态,它只是“创建动作”的入口
- 如果真需要初始化参数(如API密钥),应通过工厂类的构造函数传入,而不是让
create()方法接收一堆配置项
简单工厂用数组配置比switch更可持续
硬编码switch看着直观,但每加一个支付方式就得改工厂类、重新部署。实际项目中,把映射关系抽成配置数组更安全、更易维护。
比如这样定义config/payment_drivers.php:
<?php return [
'alipay' => \App\Payment\AlipayProcessor::class,
'wechat' => \App\Payment\WechatProcessor::class,
'unionpay'=> \App\Payment\UnionPayProcessor::class,
];
工厂内部只需加载并校验:
public static function create(string $type): PaymentInterface {
$drivers = require __DIR__ . '/config/payment_drivers.php';
$class = $drivers[$type] ?? null;
if (!$class || !class_exists($class)) {
throw new InvalidArgumentException("Unknown payment type: {$type}");
}
return new $class();
}
- 必须用
class_exists()兜底,否则输错类型(如'aliapy')会触发Fatal error,比抛异常难排查 - 配置文件可被缓存、热加载,甚至从数据库读取,灵活性远超
switch - 不要在工厂里做连接池、重试、日志——那是具体类的职责
工厂方法模式比简单工厂更适合核心业务
当不同供应商的初始化逻辑差异大(比如微信要getAccessToken(),支付宝要signRequest()),简单工厂的create()方法会越来越臃肿。这时该用工厂方法:让每个供应商自己实现创建逻辑。
例如定义抽象工厂:
abstract class PaymentFactory {
abstract public function createProcessor(): PaymentProcessor;
}
再让子类各自实现:
class WechatPaymentFactory extends PaymentFactory {
private string $appId;
public function __construct(string $appId) { $this->appId = $appId; }
public function createProcessor(): PaymentProcessor {
$client = new WechatClient($this->appId);
$client->refreshToken(); // 微信特有逻辑
return new WechatProcessor($client);
}
}
- 新增银联支付?只加
UnionPayPaymentFactory,不碰已有代码 - 工厂类本身可被依赖注入容器管理,构造参数(如
$appId)由容器注入,不硬编码 - 避免让工厂承担“判断环境然后选类”的职责——那是配置或策略模式的事
绝大多数时候,你该用依赖注入容器,而不是手写工厂
现代PHP项目里,90%的工厂场景其实该交给容器处理。比如PHP-DI或Laravel IoC,它们本身就是高级工厂:支持自动解析依赖、生命周期管理、条件绑定。
手写工厂只在两种情况必要:
- 运行时才能决定类型(如根据
$_SERVER['HTTP_X_PAYMENT_METHOD']选支付网关) - 封装第三方SDK复杂初始化(如
new PayPalClient($config)里要校验证书、设置重试策略)
其他时候,把工厂当“上帝对象”来写(一个ServiceFactory里塞20个createXXX()),只会让测试变难、职责变乱、扩展变僵。
真正容易被忽略的是:工厂不该是解耦的终点,而是连接容器与运行时决策的桥梁——它本身得可测试、可替换、不带副作用。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











