PHP 模板方法模式(Template Method Pattern)
核心概念
模板方法模式定义了一个算法的骨架,将某些步骤延迟到子类实现,子类可以重新定义算法的特定步骤,而不改变算法的结构。

基础骨架实现
1 抽象类(骨架定义)
<?php
declare(strict_types=1);
/**
* 抽象咖啡因饮料类 - 模板方法模式的骨架
*/
abstract class CaffeineBeverage
{
/**
* 模板方法 - 定义算法骨架(final防止子类重写)
*/
final public function prepareRecipe(): void
{
$this->boilWater();
$this->brew(); // 由子类实现
$this->pourInCup();
if ($this->customerWantsCondiments()) { // 钩子方法
$this->addCondiments(); // 由子类实现
}
}
/**
* 公共步骤 - 在抽象类中实现
*/
protected function boilWater(): void
{
echo " boiling water...\n";
}
protected function pourInCup(): void
{
echo " pouring into cup...\n";
}
/**
* 抽象方法 - 必须由子类实现
*/
abstract protected function brew(): void;
abstract protected function addCondiments(): void;
/**
* 钩子方法 - 默认实现,子类可覆盖
*/
protected function customerWantsCondiments(): bool
{
return true; // 默认需要调料
}
}
2 具体子类实现
<?php
declare(strict_types=1);
/**
* 茶类 - 具体实现
*/
class Tea extends CaffeineBeverage
{
protected function brew(): void
{
echo " steeping tea bag...\n";
}
protected function addCondiments(): void
{
echo " adding lemon...\n";
}
// 覆盖钩子方法
protected function customerWantsCondiments(): bool
{
$answer = readline("Would you like lemon with your tea? (y/n): ");
return strtolower($answer[0] ?? 'y') === 'y';
}
}
/**
* 咖啡类 - 具体实现
*/
class Coffee extends CaffeineBeverage
{
protected function brew(): void
{
echo " dripping coffee through filter...\n";
}
protected function addCondiments(): void
{
echo " adding sugar and milk...\n";
}
protected function customerWantsCondiments(): bool
{
$answer = readline("Would you like sugar and milk with your coffee? (y/n): ");
return strtolower($answer[0] ?? 'y') === 'y';
}
}
3 客户端使用
<?php
// 客户端代码
function makeDrink(CaffeineBeverage $beverage): void
{
echo "\n--- Preparing " . get_class($beverage) . " ---\n";
$beverage->prepareRecipe();
}
// 测试
$tea = new Tea();
$coffee = new Coffee();
makeDrink($tea);
makeDrink($coffee);
扩展骨架:订单处理示例
<?php
declare(strict_types=1);
/**
* 订单处理骨架
*/
abstract class OrderProcessTemplate
{
final public function processOrder(bool $isGift): void
{
$this->selectItems();
$this->makePayment();
$this->doDelivery();
if ($isGift) {
$this->wrapGift(); // 钩子
}
$this->sendConfirmation();
}
// 公共算法步骤
protected function selectItems(): void
{
echo " selecting items from cart...\n";
}
protected function makePayment(): void
{
echo " processing payment...\n";
}
protected function sendConfirmation(): void
{
echo " sending confirmation email...\n";
}
// 抽象方法
abstract protected function doDelivery(): void;
// 钩子方法
protected function wrapGift(): void
{
echo " wrapping gift...\n";
}
}
/**
* 在线订单
*/
class OnlineOrder extends OrderProcessTemplate
{
protected function doDelivery(): void
{
echo " scheduling courier pickup...\n";
}
protected function wrapGift(): void
{
echo " adding gift box and card...\n";
}
}
/**
* 店内自提订单
*/
class StorePickup extends OrderProcessTemplate
{
protected function doDelivery(): void
{
echo " preparing items for in-store pickup...\n";
}
// 不覆盖wrapGift,使用默认行为
}
骨架的键组成部分详解
| 组件 | 类型 | 作用 |
|---|---|---|
| 模板方法 | final public | 定义算法骨架,调用各个步骤 |
| 抽象方法 | abstract | 必须由子类实现的具体步骤 |
| 具体方法 | protected/public | 算法中不变的部分,基类实现 |
| 钩子方法 | protected | 可选步骤,默认实现或空实现 |
模板方法 vs 策略模式
| 比较项 | 模板方法 | 策略模式 |
|---|---|---|
| 复用级别 | 类级别(继承) | 对象级别(组合) |
| 控制权 | 父类控制算法结构 | 客户端控制策略对象 |
| 扩展方式 | 子类覆盖步骤 | 实现新策略类 |
| 代码结构 | 继承框架 | 委托/组合 |
实战建议
✅ 应该在以下场景使用:
- 算法有固定步骤,但部分步骤因对象而异
- 多个类有共同的算法逻辑,需要提取公共部分
- 需要子类扩展特定步骤,但不允许改变算法结构
⚠️ 注意事项:
- 模板方法使用
final防止子类破坏算法结构 - 钩子方法应默认提供合理行为,减少子类负担
- 避免模板方法过于庞大,保持步骤单一职责
- 考虑抽象方法数量,过多的抽象方法会增加子类负担
完整示例:数据迁移骨架
<?php
declare(strict_types=1);
/**
* 数据库迁移模板
*/
abstract class DatabaseMigrator
{
final public function migrate(): void
{
$this->beginTransaction();
try {
$this->runMigrations();
$this->updateVersionHistory();
$this->commitTransaction();
if ($this->shouldOutputLog()) {
$this->logSuccess();
}
} catch (Throwable $e) {
$this->rollbackTransaction();
$this->logFailure($e);
throw $e;
}
}
protected function beginTransaction(): void
{
echo " starting transaction...\n";
}
protected function commitTransaction(): void
{
echo " committing transaction...\n";
}
protected function rollbackTransaction(): void
{
echo " rollback transaction...\n";
}
protected function logSuccess(): void
{
echo " migration succeeded.\n";
}
protected function logFailure(Throwable $e): void
{
echo " migration failed: " . $e->getMessage() . "\n";
}
// 钩子
protected function shouldOutputLog(): bool
{
return true;
}
abstract protected function runMigrations(): void;
abstract protected function updateVersionHistory(): void;
}
/**
* MySQL 迁移器
*/
class MySQLMigrator extends DatabaseMigrator
{
protected function runMigrations(): void
{
echo " running MySQL specific SQL migrations...\n";
}
protected function updateVersionHistory(): void
{
echo " updating migration_history table (MySQL)...\n";
}
}
关键要点总结
- 骨架保护:模板方法设为
final,子类不可覆盖 - 抽象分离:将变化的部分抽象为方法,由子类实现
- 钩子灵活性:钩子方法提供可选扩展点,子类可选择性覆盖
- 依赖倒置:子类实现细节,父类定义流程(好莱坞原则)
- 注意边界:过多步骤或过多抽象方法会使类层次难以维护
模板方法模式非常适合代码复用和框架设计场景,尤其是在需要固定流程又有扩展需求时,能有效避免代码重复,同时保证算法结构的稳定性。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播