引言
设计模式不是「背 23 个套路」,而是反复出现的结构问题的最优解。PHP 8 的类型系统(属性、联合类型、接口默认实现)让模式落地更干净。本文先立 SOLID 原则(所有模式的灵魂),再按创建/结构/行为三族过一遍 PHP 8 写法,最后拆解 Laravel 容器里实际藏着的模式,让你读懂框架也写出自己的好设计。
前置:/php8-modern-features/(PHP 8 语法)、/php-laravel-internals/(容器与门面)。
目录
- 1. OOP 与 PHP 8 的类型基础
- 2. SOLID 五原则:模式的灵魂
- 3. 创建型模式:单例、工厂、建造者
- 4. 结构型模式:适配器、装饰器、组合
- 5. 行为型模式:策略、观察者、模板方法
- 6. 依赖注入与容器:反模式的解药
- 7. Laravel 内置模式解析
- 8. 模式选型与反模式
- 9. 实战:订单系统重构
- 10. 速查表
- 延伸阅读
1. OOP 与 PHP 8 的类型基础
PHP 8 让 OOP 表达更安全:属性(properties)替代 getter/setter 样板、联合类型表达「多态值」、只读类杜绝可变状态。
// PHP 8 构造器属性提升 + 只读类
readonly class Money
{
public function __construct(
public readonly int $amount,
public readonly string $currency,
) {}
}
// 联合类型:参数接受多种形态
public function pay(Money|CreditCard $payment): void { ... }
接口默认方法(8.0+)让抽象演进更平滑:
interface Logger {
public function log(string $msg): void;
public function debug(string $msg): void
{ $this->log("[DEBUG] $msg"); } // 默认实现
}
心智:类型先收窄,模式后展开——先让「非法状态不可表达」,模式才有意义。
2. SOLID 五原则:模式的灵魂
| 原则 | 含义 | 违反的症状 |
|---|---|---|
| S 单一职责 | 一个类只有一条变更理由 | 「上帝类」塞了一堆职责 |
| O 开闭 | 对扩展开放、对修改关闭 | 每加功能就改旧类 |
| L 里氏替换 | 子类必须能替换父类 | 子类改坏父类契约 |
| I 接口隔离 | 接口要小,别让实现做无用的 | 巨型接口逼实现空实现 |
| D 依赖倒置 | 依赖抽象,不依赖具体 | 高层直接 new 低层 |
依赖倒置例子——高层依赖接口:
// 坏:高层依赖具体实现,改支付方式要改业务代码
class Checkout {
public function __construct(private StripePayment $payment) {}
}
// 好:依赖抽象接口
interface PaymentGateway {
public function charge(int $amount): Receipt;
}
class Checkout {
public function __construct(private PaymentGateway $payment) {}
}
记忆:SOLID 是「为什么」,模式是「怎么做」——违反 SOLID 时,往往正需要某个模式来修复。
3. 创建型模式:单例、工厂、建造者
单例(Singleton)——全局唯一实例(慎用,测试难):
final class Config {
private static ?self $instance = null;
public static function get(): self {
return self::$instance ??= new self();
}
private function __construct() {
$this->load(env('APP_ENV'));
}
}
工厂(Factory)——按条件创建对象(开闭的典型):
interface Notifier { public function send(string $to, string $msg): void; }
class SmsNotifier implements Notifier { ... }
class EmailNotifier implements Notifier { ... }
class NotifierFactory {
public function create(string $type): Notifier {
return match ($type) {
'sms' => new SmsNotifier(),
'email' => new EmailNotifier(),
default => throw new InvalidArgumentException("未知通知方式 $type"),
};
}
}
建造者(Builder)——分步构建复杂对象:
class QueryBuilder {
private array $clauses = [];
public function where(string $col, mixed $val): self {
$this->clauses[] = "$col = ?";
return $this; // 链式
}
public function get(): string {
return 'WHERE ' . implode(' AND ', $this->clauses);
}
}
$q = (new QueryBuilder())->where('a', 1)->where('b', 2);
| 模式 | 场景 | PHP 注意 |
|---|---|---|
| 单例 | 全局配置/日志 | 构造函数私有,测试用静态重置 |
| 工厂 | 按类型创建 | match 让工厂干净 |
| 抽象工厂 | 多系列产品 | 接口族 + 实现族 |
| 建造者 | 复杂参数对象 | readonly + 链式 : self |
4. 结构型模式:适配器、装饰器、组合
适配器(Adapter)——统一不同接口(第三方集成万能):
// 老系统邮件接口
class LegacyMailer { public function sendMail(string $to, string $body): void {} }
// 适配到统一接口
class LegacyMailerAdapter implements Mailer {
public function __construct(private LegacyMailer $inner) {}
public function send(string $to, string $msg): void {
$this->inner->sendMail($to, $msg); // 翻译调用
}
}
装饰器(Decorator)——给对象动态加功能(替代继承爆炸):
interface Middleware { public function handle(): void; }
class LoggingDecorator implements Middleware {
public function __construct(private Middleware $inner) {}
public function handle(): void {
$start = microtime(true);
$this->inner->handle();
echo '耗时: ' . (microtime(true) - $start) . 's';
}
}
$pipeline = new LoggingDecorator(new RateLimitDecorator(new RealHandler()));
组合(Composite)——树状结构统一处理:
interface Node { public function render(): string; }
class Leaf implements Node { ... }
class Branch implements Node {
private array $children = [];
public function render(): string {
return implode('', array_map(fn($c) => $c->render(), $this->children));
}
}
记忆:适配器「翻译接口」、装饰器「包裹增强」、组合「树状递归」——结构型模式管的是对象之间的关系形态。
5. 行为型模式:策略、观察者、模板方法
策略(Strategy)——算法可切换(替代大量 if/else):
interface SortStrategy { public function sort(array &$data): void; }
class QuickSort implements SortStrategy { ... }
class BubbleSort implements SortStrategy { ... }
class Sorter {
public function __construct(private SortStrategy $strategy) {}
public function setStrategy(SortStrategy $s): void { $this->strategy = $s; }
public function apply(array &$data): void { $this->strategy->sort($data); }
}
观察者(Observer)——事件驱动解耦(Laravel 事件系统核心):
interface Subscriber {
public function notify(string $event, array $payload): void;
}
class EventBus {
private array $subscribers = [];
public function subscribe(Subscriber $s): void { $this->subscribers[] = $s; }
public function dispatch(string $event, array $payload): void {
foreach ($this->subscribers as $s) { $s->notify($event, $payload); }
}
}
// 订单创建后广播,日志/邮件/库存各自订阅
$bus = new EventBus();
$bus->subscribe(new EmailSubscriber());
$bus->subscribe(new InventorySubscriber());
$bus->dispatch('order.created', ['orderId' => 1]);
模板方法(Template Method)——骨架固定、步骤可覆写:
abstract class DataImporter {
final public function import(): void { // 骨架
$data = $this->fetch();
$this->validate($data);
$this->persist($data);
}
abstract protected function fetch(): array;
abstract protected function persist(array $data): void;
protected function validate(array $data): void {} // 默认空实现
}
| 模式 | 一句话 | Laravel 对应 |
|---|---|---|
| 策略 | 算法切换 | 队列连接、缓存驱动 |
| 观察者 | 事件广播 | Event/Listener |
| 模板方法 | 固定骨架 | 抽象 Service |
| 命令 | 操作封装 | Job 队列 |
| 状态 | 状态迁移 | 订单状态机 |
6. 依赖注入与容器:反模式的解药
依赖注入(DI):对象不自己 new 依赖,由外部注入——配合容器实现 D 原则:
// 不使用 DI:类内 new,测试只能真依赖
class OrderService {
public function __construct() {
$this->repo = new OrderRepository();
}
}
// 使用 DI:构造注入,测试可换 fake
class OrderService {
public function __construct(
private OrderRepository $repo, // 依赖被注入
) {}
}
容器(Container):自动解析依赖图:
// 简化容器
class Container {
private array $bindings = [];
public function bind(string $abstract, callable $factory): void {
$this->bindings[$abstract] = $factory;
}
public function make(string $abstract): object {
return ($this->bindings[$abstract])($this);
}
}
$c = new Container();
$c->bind(PaymentGateway::class, fn() => new StripeGateway('sk_test_...'));
$checkout = new Checkout($c->make(PaymentGateway::class));
Laravel 的容器正是这个思路的工程版(反射自动解析 + 绑定覆盖),深入见 /php-laravel-internals/。
7. Laravel 内置模式解析
读懂框架即读懂模式:
| Laravel 组件 | 模式 | 用途 |
|---|---|---|
| Service Container | 依赖注入 + 服务定位 | 自动解析依赖 |
| Facade | 门面(Facade 模式) | 静态式访问容器单例 |
| Pipeline | 责任链 | 中间件洋葱模型 |
| Event/Listener | 观察者 | 解耦业务动作 |
| Queue/Job | 命令模式 | 异步任务封装 |
| Repository(约定) | 仓储 | 数据访问抽象 |
| Strategy(cache/queue 驱动) | 策略 | 同接口多实现切换 |
自己写代码照抄的结构:
// 服务提供者 = 容器的「装配点」
class OrderServiceProvider extends ServiceProvider {
public function register(): void {
$this->app->bind(PaymentGateway::class, fn() =>
new StripeGateway(config('payment.stripe_key')));
}
}
// 控制器只依赖接口,换实现不动业务
class OrderController {
public function __construct(private PaymentGateway $payment) {}
}
价值:理解模式 = 理解框架为何长这样,遇到奇怪写法不再「照抄但不懂」。
8. 模式选型与反模式
选型问题:每个模式有成本(类变多、间接层变厚),别为「时尚」用模式。
反模式大忌:
| 反模式 | 危害 |
|---|---|
| 上帝类 | 职责混乱、无法测试 |
| 单例滥用 | 全局状态、测试互污染 |
| 过度抽象 | 接口套接口,没人读懂 |
| 继承当复用 | 深继承树、脆裂 |
| God Object | 一个对象什么都能做 |
| 双亲调用链 | 构造逻辑深埋 |
什么时候别用模式:
□ 只有一个实现 → 别抽象接口
□ 三年没换过策略 → 别套 Strategy
□ 团队读不懂 → 简单直白优于「标准」
记忆:模式是工具箱,不是装饰品。先写简单正确的代码,出现「变一次要改十处」时再上模式。
9. 实战:订单系统重构
重构目标:把 200 行的 OrderController 拆成可测试、可扩展的结构。
步骤 1:依赖倒置——支付抽象化:
interface PaymentGateway {
public function charge(Order $order): Receipt;
}
步骤 2:策略——多渠道通知:
class OrderProcessor {
public function __construct(
private PaymentGateway $payment,
private array $notifiers, // Notifier[]
) {}
public function process(Order $order): void {
$receipt = $this->payment->charge($order);
foreach ($this->notifiers as $n) {
$n->send($order->customerEmail(), "订单 {$order->id} 支付成功");
}
$this->emit('order.processed', $order, $receipt);
}
}
步骤 3:观察者——后续动作全部订阅:
$bus->subscribe(new OrderConfirmationMailer());
$bus->subscribe(new StockDecrementer());
$bus->subscribe(new AnalyticsTracker());
结果对比:
| 维度 | 重构前 | 重构后 |
|---|---|---|
| 新增支付方式 | 改 controller | 加一个实现 + 绑定 |
| 新增通知渠道 | 改 controller | 加订阅者 |
| 单元测试 | 难(真依赖) | 注入 fake 全测 |
| 职责 | 上帝类 | 各司其职 |
10. 速查表
| 需求 | 模式 |
|---|---|
| 全局唯一实例 | 单例(慎用) |
| 按类型创建对象 | 工厂 / 抽象工厂 |
| 分步构建复杂对象 | 建造者 |
| 统一异构接口 | 适配器 |
| 动态加功能 | 装饰器 |
| 树状结构统一处理 | 组合 |
| 算法可切换 | 策略 |
| 事件解耦 | 观察者 |
| 固定骨架可覆写 | 模板方法 |
| 解耦依赖 | 依赖注入 + 容器 |
一句话记忆:SOLID 定规矩,创建型管「怎么造」、结构型管「怎么连」、行为型管「怎么协作」;依赖注入是 D 原则的落地,容器是注入的自动机。
延伸阅读
- /php-laravel-internals/ — 容器/门面/管道的模式内核
- /php8-modern-features/ — PHP 8 类型让模式更安全
- /php-testing-practice/ — 依赖注入让测试变简单
- /php-microservices-message-queue/ — 观察者/命令在分布式中的延伸
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。