引言:为什么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; // 添加新类型时——需要修改类! } }