工厂函数必须返回具体类实例,类型需与接口/抽象类一致,避免返回object或空值;应封装创建逻辑而非初始化,支持动态子类选择,集中管理对象创建以提升可维护性与可测试性。

工厂函数该返回什么类型
工厂函数的核心职责不是“造对象”,而是“明确谁来造、造什么、怎么造”。它必须返回一个具体类的实例,不能只返回 object 或留空。否则后续调用方法时会触发 Fatal error: Uncaught Error: Call to undefined method。
- 返回类型必须和接口/抽象类一致(如果用了类型声明),比如
public function create(): PaymentProcessor - 避免在工厂里做复杂初始化逻辑——那是构造函数或
init()方法的事 - 若需动态决定返回子类,用
switch或配置映射,别硬编码 new 一堆类
为什么不能把 new 写在业务代码里
直接 new AlipayService() 看似简单,但只要需求变:比如要切到微信支付、加个 Mock 支付用于测试、或按地区切换网关,就得改所有调用点。工厂把“创建权”收归一处,业务代码只认接口。
- 测试时可传入
MockPaymentProcessor,不用改业务逻辑 - 环境配置(如
APP_ENV=testing)可驱动工厂返回不同实现 - 若某支付 SDK 升级导致构造参数变化,只需改工厂,不碰控制器或服务类
简单工厂 vs 抽象工厂怎么选
90% 的 PHP 项目用“简单工厂”就够了——一个函数,输入标识,输出对象。抽象工厂是为“一族相关对象”设计的(比如 WindowsButton + WindowsDialog),PHP 里极少需要。
- 简单工厂:用
PaymentFactory::create('alipay'),返回单个对象 - 抽象工厂:要定义
GuiFactory接口,再实现WinFactory和MacFactory,维护成本高 - Laravel 的
app()->make()是容器级抽象,不是手写工厂的替代品——它解决的是生命周期管理,不是解耦创建逻辑
容易被忽略的错误:工厂里没处理异常
工厂函数看似只是 new 一下,但如果构造函数抛出异常(比如配置缺失、SDK 初始化失败),而工厂没捕获,错误堆栈会指向工厂内部,掩盖真实问题位置。
- 在工厂中用
try/catch包裹 new,并重新 throw 带上下文的异常,例如:throw new InvalidArgumentException("Unknown driver: $driver") - 不要静默吞掉异常,也不要直接
die()或exit() - 若工厂依赖配置,提前校验必要字段(如
config('payment.alipay.app_id')是否存在),比等到 new 时再报错更易调试
工厂不是语法糖,它是把“对象从哪来”这个决策点显式拎出来。一旦开始写 new,就要问自己:这个类的实例化逻辑,未来会不会变?变的时候,我希望改几处?
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











