PHP中的SOLID原则:高级应用与实践示例

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:为什么SOLID不仅仅是教条,而是一种工具

大多数开发人员都知道,SOLID是面向对象编程的五个原则:单一职责、开闭原则、里氏替换、接口隔离和依赖倒置。然而在实践中,理解往往归结为机械地遵循规则。在本文中,我们将更进一步:分析PHP中SOLID的高级应用场景,审视违反原则的反模式,并展示基于SOLID的重构如何使代码变得灵活且可测试。

本文面向中级以上和高级开发人员,他们已经在使用PHP编写代码,并希望改进其项目的架构。



1. 单一职责原则 (SRP):当类“知道得太多”时

经典表述:“一个类应该只有一个引起它变化的原因”。在高级层面,重要的是要理解“引起变化的原因”是一个业务需求。如果你的类因为验证规则、输出格式和保存逻辑的变化而改变,那就违反了SRP。



违反示例 (上帝对象)

class OrderProcessor {    public function process(array $data): void {        // 1. 数据验证        if (empty($data['user_id']) || empty($data['items'])) {            throw new InvalidArgumentException('Invalid order data');        }                // 2. 计算总价        $total = 0;        foreach ($data['items'] as $item) {            $total += $item['price'] * $item['quantity'];        }                // 3. 保存到数据库        $sql = "INSERT INTO orders (user_id, total, status) VALUES (?, ?, 'pending')";        // ... 执行查询                // 4. 发送邮件        mail($data['user_email'], 'Order confirmation', "Total: $total");    }}


基于SRP的重构

拆分为三个职责明确的类:

class OrderValidator {    public function validate(array $data): void {        if (empty($data['user_id']) || empty($data['items'])) {            throw new InvalidArgumentException('Invalid order data');        }    }}

class OrderCalculator { public function calculateTotal(array $items): float { return array_reduce($items, function ($carry, $item) { return $carry + $item['price'] * $item['quantity']; }, 0.0); }}

class OrderRepository { public function save(int $userId, float $total, string $status = 'pending'): int { // 保存到数据库的逻辑 return 42; // 订单ID }}

class OrderProcessor { public function __construct( private OrderValidator $validator, private OrderCalculator $calculator, private OrderRepository $repository, private Mailer $mailer ) {} public function process(array $data): void { $this->validator->validate($data); $total = $this->calculator->calculateTotal($data['items']); $orderId = $this->repository->save($data['user_id'], $total); $this->mailer->send($data['user_email'], 'Order #' . $orderId); }}


2. 开闭原则 (OCP):对扩展开放,对修改关闭

OCP的实质:类应该对扩展开放,但对修改关闭。在实践中,这意味着使用多态和抽象而不是条件结构。



违反OCP:针对每种新类型的switch-case

class PaymentProcessor {    public function pay(string $type, float $amount): void {        switch ($type) {            case 'credit_card':                // 处理信用卡                break;            case 'paypal':                // 处理PayPal                break;            // 添加新类型时——需要修改类!        }    }

博客

书籍推荐