SSM框架房屋租赁系统实战复盘:从数据库设计到并发事务
2026/9/29 17:53:46
PHP 中的“工厂模式” ≠ “抽象工厂” ≠ “简单工厂”。
三者虽同属创建型设计模式,但抽象层级、适用场景、解决的问题截然不同。混淆它们,会导致过度设计或抽象不足。
| 模式 | 核心意图 | 问题场景 |
|---|---|---|
| 简单工厂(Simple Factory) | 封装对象创建逻辑 | 客户端不想直接new,但产品类型固定 |
| 工厂方法(Factory Method) | 将对象创建延迟到子类 | 产品族单一,但创建逻辑需子类定制 |
| 抽象工厂(Abstract Factory) | 创建一系列相关/依赖对象 | 产品族多维,需保证组合一致性 |
🔑关键区分:
- 简单工厂:1 个工厂,N 个产品;
- 工厂方法:N 个工厂(子类),1 个产品线;
- 抽象工厂:1 个工厂,N×M 个产品族。
+----------------+ | SimpleFactory | +----------------+ | + create(type) | +----------------+ | v +--------+--------+ | ProductA | ProductB | +------------------++----------------+ +---------------------+ | Creator |<------| ConcreteCreatorA | +----------------+ +---------------------+ | + factoryMethod()| | + factoryMethod() | +----------------+ +---------------------+ | | v v +----------------+ +---------------------+ | Product |<------| ConcreteProductA | +----------------+ +---------------------++------------------+ +-----------------------+ | AbstractFactory |<------| ConcreteFactoryA | +------------------+ +-----------------------+ | + createProductA()| | + createProductA() | | + createProductB()| | + createProductB() | +------------------+ +-----------------------+ | | v v +------------------+ +-----------------------+ | AbstractProductA |<------| ConcreteProductA1 | | AbstractProductB |<------| ConcreteProductB1 | +------------------+ +-----------------------+// 产品接口interfaceLogger{publicfunctionlog(string$message);}// 具体产品classFileLoggerimplementsLogger{publicfunctionlog(string$message){/* 写文件 */}}classDbLoggerimplementsLogger{publicfunctionlog(string$message){/* 写 DB */}}// 简单工厂classLoggerFactory{publicstaticfunctioncreate(string$type):Logger{returnmatch($type){'file'=>newFileLogger(),'db'=>newDbLogger(),default=>thrownewException('Invalid logger type'),};}}// 使用$logger=LoggerFactory::create('file');RedisLogger需改工厂。// 抽象 CreatorabstractclassLoggerFactory{abstractpublicfunctioncreateLogger():Logger;publicfunctionlog(string$message){$this->createLogger()->log($message);}}// 具体 CreatorclassFileLoggerFactoryextendsLoggerFactory{publicfunctioncreateLogger():Logger{returnnewFileLogger();}}classDbLoggerFactoryextendsLoggerFactory{publicfunctioncreateLogger():Logger{returnnewDbLogger();}}// 使用$factory=newFileLoggerFactory();$factory->log('Hello');// 抽象产品族interfaceButton{publicfunctionrender();}interfaceCheckbox{publicfunctionrender();}// 具体产品族classWinButtonimplementsButton{publicfunctionrender(){echo"Win Button";}}classWinCheckboximplementsCheckbox{publicfunctionrender(){echo"Win Checkbox";}}classMacButtonimplementsButton{publicfunctionrender(){echo"Mac Button";}}classMacCheckboximplementsCheckbox{publicfunctionrender(){echo"Mac Checkbox";}}// 抽象工厂interfaceGUIFactory{publicfunctioncreateButton():Button;publicfunctioncreateCheckbox():Checkbox;}// 具体工厂classWinFactoryimplementsGUIFactory{publicfunctioncreateButton():Button{returnnewWinButton();}publicfunctioncreateCheckbox():Checkbox{returnnewWinCheckbox();}}classMacFactoryimplementsGUIFactory{publicfunctioncreateButton():Button{returnnewMacButton();}publicfunctioncreateCheckbox():Checkbox{returnnewMacCheckbox();}}// 使用$factory=newWinFactory();$button=$factory->createButton();$checkbox=$factory->createCheckbox();| 场景 | 推荐模式 | 原因 |
|---|---|---|
| 日志驱动(File/DB/Redis) | 简单工厂 | 产品类型固定,无需子类扩展 |
| 多环境配置(Dev/Staging/Prod) | 工厂方法 | 每环境创建逻辑不同,需子类定制 |
| UI 主题切换(Win/Mac/iOS) | 抽象工厂 | 需保证按钮+文本框+下拉框风格一致 |
| 支付网关(Alipay/WeChat/PayPal) | 简单工厂 or 工厂方法 | 若仅创建支付对象 → 简单工厂;若需创建支付+通知对象 → 抽象工厂 |
💡Laravel 中的实践:
- 简单工厂:
Cache::store('redis');- 工厂方法:
EventServiceProvider的shouldDiscoverEvents();- 抽象工厂:极少直接使用,但服务容器(Service Container) 可替代其功能。
new更清晰;不要问“该用哪种工厂”,
而要问“当前问题需要什么抽象”。
真正的工程能力,
不在“知道模式”,
而在“知道何时不用模式”。
这,才是 PHP 程序员的设计智慧。