PHP 面向对象与设计模式:SOLID、常用模式与 Laravel 实践

系统覆盖 PHP OOP 与设计模式:SOLID 五原则、创建型/结构型/行为型经典模式、依赖注入与容器、Laravel 内置模式解析、选型与反模式,附订单系统重构实战。

引言

设计模式不是「背 23 个套路」,而是反复出现的结构问题的最优解。PHP 8 的类型系统(属性、联合类型、接口默认实现)让模式落地更干净。本文先立 SOLID 原则(所有模式的灵魂),再按创建/结构/行为三族过一遍 PHP 8 写法,最后拆解 Laravel 容器里实际藏着的模式,让你读懂框架也写出自己的好设计。

前置:/php8-modern-features/(PHP 8 语法)、/php-laravel-internals/(容器与门面)。


目录


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/ — 观察者/命令在分布式中的延伸

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「php」更多文章

  1. PHP 静态分析与代码质量:PHPStan、Psalm、Rector 与 CI 门禁
  2. PHP 部署运维实战:Nginx、PHP-FPM、Docker 与 CI/CD
  3. PHP 缓存与 Redis 实战:缓存模式、数据结构与一致性