首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 面向对象设计五大原则(SOLID)与属性封装实战:从「能跑」到「可维护」

     編輯:PHP 相關知識

SOLID 原则在 PHP 中的落地

原则核心含义PHP 实践要点
S 单一职责 (SRP)一个类只做一件事,只有一个引起变化的原因拆分 God Class,Service/Repository/Entity 分层,Trait 复用横切逻辑
O 开闭原则 (OCP)对扩展开放,对修改关闭接口/抽象类定义契约,策略模式/装饰器模式注入新行为,避免修改核心类
L 里氏替换 (LSP)子类必须能替换父类而不破坏程序正确性严格遵守父类契约,不强化前置条件、不弱化后置条件,慎用继承优先组合
I 接口隔离 (ISP)依赖精准接口,不依赖不需要的方法拆分大接口为多个小接口(如 LoggerInterface、CacheableInterface),类实现所需接口
D 依赖倒置 (DIP)高层模块不依赖低层实现,均依赖抽象构造函数注入接口,使用 Symfony/Illuminate 容器自动装配,避免 new 具体类

属性封装:从 public 变量到 Getter/Setter 的进化

反面教材:直接暴露 public 属性

<?php
class ShopProduct {
    public $title = 'default';
    public $price = 0;
}

$p = new ShopProduct();
$p->title = "My Book";      // 无拼写检查
$p->price = -99;            // 无业务校验
$p->publicher = "Unknown";  // 动态属性,拼写错误不报错

弊端:拼写错误静默失败、无校验逻辑、无法延迟加载、破坏封装、重构极难。

进阶:Getter/Setter 封装 + 类型声明

<?php
class ShopProduct
{
    private string $title = 'default';
    private float $price = 0.0;

    public function getTitle(): string
    {
        return $this->title;
    }

    public function setTitle(string $title): self
    {
        $this->title = trim($title); // 自动清理
        return $this; // 链式调用
    }

    public function getPrice(): float
    {
        return $this->price;
    }

    public function setPrice(float $price): self
    {
        if ($price < 0) {
            throw new \InvalidArgumentException('Price cannot be negative');
        }
        $this->price = $price;
        return $this;
    }

    // 只读属性:仅 Getter
    public function getFormattedPrice(): string
    {
        return '¥' . number_format($this->price, 2);
    }
}

// 使用
$p = new ShopProduct();
$p->setTitle("  Clean Code  ")->setPrice(59.9);
echo $p->getTitle();        // "Clean Code"
echo $p->getFormattedPrice(); // "¥59.90"

现代 PHP 8.1+:readonly 属性 + 构造函数提升

<?php
class ImmutableProduct
{
    public function __construct(
        public readonly string $title,
        public readonly float $price,
        public readonly \DateTimeImmutable $publishedAt
    ) {}

    // 无需 Getter/Setter,外部不可修改,内部也不可修改
    public function withPrice(float $newPrice): self
    {
        return new self($this->title, $newPrice, $this->publishedAt); // 返回新实例
    }
}

$p = new ImmutableProduct("Domain-Driven Design", 79.0, new \DateTimeImmutable());
$p2 = $p->withPrice(69.0); // 价格变更创建新对象,原对象不变

依赖倒置实战:接口 + 容器自动装配

<?php
// 定义契约
interface PaymentGatewayInterface
{
    public function charge(float $amount, string $currency): TransactionResult;
}

// 具体实现
class StripeGateway implements PaymentGatewayInterface { /* ... */ }
class AlipayGateway implements PaymentGatewayInterface { /* ... */ }

// 高层服务依赖抽象
class OrderService
{
    public function __construct(
        private PaymentGatewayInterface $gateway,
        private LoggerInterface $logger
    ) {}

    public function pay(Order $order): void
    {
        $result = $this->gateway->charge($order->total(), 'CNY');
        $this->logger->info('Payment processed', ['order_id' => $order->id()]);
    }
}

// 容器配置(Symfony/Illuminate 自动装配无需显式配置)
// services.yaml
// services:
//     App\Service\OrderService:
//         arguments:
//             $gateway: '@App\Payment\StripeGateway' // 或通过环境变量切换

配置化替代硬编码

<?php
// 差:硬编码
class EmailSender {
    public function send() { mail('admin@example.com', ...); }
}

// 好:配置注入
class EmailSender {
    public function __construct(
        private string $fromAddress,
        private string $adminAddress,
        private string $smtpHost,
        private int $smtpPort
    ) {}
    // 从 .env / config.yaml 注入
}

面向接口编程:只关心契约,不关心实现

<?php
// 控制器/服务中
public function export(ExporterInterface $exporter): string
{
    return $exporter->export($this->data);
}

// 运行时可切换:CsvExporter、ExcelExporter、PdfExporter
// 测试时注入 MockExporter 验证交互,无需真实 IO

总结:从代码到架构的思维跃迁

  1. 封装变化:将「易变的业务规则」封装在 Setter/Value Object 中,而非散落在调用处。
  2. 依赖抽象:构造函数要求接口,容器负责绑定实现,实现「编译期依赖抽象、运行时绑定实现」。
  3. 组合优于继承:用 Strategy/Decorator/Composite 组合行为,避免深层继承树。
  4. 不可变优先:PHP 8.1 readonly、DateTimeImmutable、Value Object 减少副作用,天然线程安全。
  5. 测试驱动设计:能轻松 Mock 的代码,通常符合 SOLID;难以测试的代码,大概率违反 DIP/ISP。
「好的架构不是预先设计出来的,而是在不断重构中演化出来的。」—— 从今天开始,每次修改代码前问自己:这是否违反了 SOLID?
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved