factory模式在codeigniter中是解耦对象创建的实践:ci3中request::factory()专用于hmvc子请求,ci4中service()才是通用依赖工厂,自定义工厂应职责清晰、避免副作用。

Factory 模式在 CodeIgniter 中不是框架内置的“设计模式开关”,而是指一种按需构造对象、解耦实例创建逻辑的实践方式。它在 CI 3 和 CI 4 中表现不同,但核心目的相同:避免 new 硬编码,提升可测试性与模块隔离能力。
CI 3 里 Request::factory() 是 HMVC 的关键入口
这是最常被搜索到的 Factory 用法,但它只存在于 HMVC 扩展(如 Modular Extensions)中,并非 CI 3 原生功能。
- 它用于发起子请求,比如从控制器内调用另一个模块的逻辑:
$response = Request::factory('module/user/profile')->execute()->response(); - 调用路径
'module/user/profile'实际映射到app/Modules/Module/Controllers/User.php的profile()方法 -
execute()触发完整子生命周期(路由→过滤→控制器→视图),返回的是Response对象,不是原始数据 - 常见错误:直接
echo $response报错——必须用$response->body()或$response->output()提取内容
注意:Request::factory() 不是通用对象工厂,它专为 HMVC 子请求设计;想工厂化普通类,得自己写或借助第三方库。
CI 4 的 service() 才是真正的依赖工厂
CI 4 内置服务容器,service() 函数就是它的对外工厂接口,所有核心服务(如 cache、session、database)都通过它提供。
- 注册自定义服务:在
app/Config/Services.php中添加静态方法,例如:public static function myLogger() { return new \App\Libraries\MyLogger(); } - 使用时直接调用:
$logger = service('myLogger'); - 容器会自动缓存单例(除非显式配置为每次新建),且支持类型提示自动注入(如构造函数参数为
MyLogger $logger,容器能自动解析)
容易踩的坑:
- 服务类未加命名空间或
use,导致service()返回null - 忘记在
Services.php中声明方法,调用时抛出Call to undefined method - 误以为
service()是全局变量访问器——它其实是延迟初始化+单例管理器,不是简单return $instance
自己写轻量级工厂类要避开三个硬伤
当项目需要工厂化业务类(如不同支付网关、不同导出格式处理器),不建议手写带反射的复杂工厂,而应聚焦职责清晰和可维护性:
- 工厂方法名必须明确表达意图,例如
PaymentFactory::getGateway('alipay'),而不是create('alipay') - 避免在工厂里做条件判断后
new大量具体类——应把创建逻辑委托给服务容器或配置驱动 - 不要让工厂承担配置加载、连接初始化等副作用;这些应由被创建对象自身处理,或通过构造参数传入
一个安全示例:
class ExportFactory
{
public static function create(string $format): ExporterInterface
{
return match ($format) {
'xlsx' => new PhpSpreadsheetExporter(),
'csv' => new CsvExporter(),
default => throw new InvalidArgumentException("Unsupported format: {$format}"),
};
}
}
调用:$exporter = ExportFactory::create($request->getPost('format'));
为什么别在 CI 3 里强行模仿 CI 4 的 service()?
CI 3 缺乏自动依赖解析和 PSR-11 兼容容器,硬套会导致:
- 类加载混乱:
require_once路径错误、命名空间冲突 - 无法复用核心服务(如
database)的单例行为,每次new都新建连接 - 测试困难:工厂返回的对象无法被 Mock 替换,因为没有接口抽象或注入点
务实做法是:CI 3 用 $this->load->library() + 自定义 __construct() 参数传入依赖;CI 4 直接上 service() + 构造函数注入。混用两套逻辑只会增加维护成本。











