PHP 之高级面向对象编程 深入理解设计模式、原则与性能优化

PHP 之高级面向对象编程 深入理解设计模式、原则与性能优化

面向对象编程(Object-Oriented Programming, OOP)不只是“如何写 class”的语法规则。它更像一种组织软件系统的思维方式:通过清晰的边界、职责拆分与对象协作,让系统更容易理解、扩展和维护。

当你已经掌握了类、对象、属性、方法这些基础概念之后,就可以把视角往更深一层挪一挪:设计模式、SOLID 原则,以及在大型系统里绕不开的性能与内存问题。

本文会围绕几个常见主题展开:策略模式与单例模式、SOLID 五原则,以及不可变对象与内存管理相关的性能考虑。它们的目的不是“炫技”,而是让真实项目在后续迭代中更稳、更好改。

原文链接 PHP 之高级面向对象编程 深入理解设计模式、原则与性能优化

面向对象编程中的设计模式

设计模式是对软件设计中常见问题的可复用解决方案。它们不是代码模板,而是可以在不同场景下调整和落地的一组设计思路。

策略模式:将算法与类解耦

策略模式(Strategy Pattern)可以把一组可互换的算法抽出来,用统一的接口对外暴露,让使用方在运行时自由选择实现,而不需要把具体算法硬编码进业务类。

当你希望“根据条件切换算法”,又不希望让一个类膨胀到塞满 if/else 时,策略模式通常很好用。

以电商折扣为例:可能有“打折”“买一送一”等不同折扣策略。如果把所有折扣规则都堆在 Cart 里,Cart 会越来越难维护。用策略模式则可以把折扣计算拆到不同策略类里。

/* by yours.tools - online tools website : yours.tools/zh/calorie.html */
<?php

interface DiscountStrategy {
    public function apply(float $total): float;
}

class PercentDiscount implements DiscountStrategy {
    public function __construct(private float $percent) {}

    public function apply(float $total): float {
        return $total * (1 - $this->percent);
    }
}

class BuyOneGetOneFree implements DiscountStrategy {
    public function apply(float $total): float {
        // 简单模拟:买一送一即总价减半
        return $total / 2;
    }
}

class Cart {
    private array $items = [];

    // 依赖注入:注入策略接口而非具体实现
    public function __construct(private DiscountStrategy $strategy) {}

    public function addItem(float $price): void {
        $this->items[] = $price;
    }

    public function total(): float {
        $subtotal = array_sum($this->items);
        return $this->strategy->apply($subtotal);
    }
}

// 使用示例
$cart = new Cart(new PercentDiscount(0.1)); // 打九折
$cart->addItem(100);
$cart->addItem(200);
echo $cart->total(); // 输出: 270

这里 Cart 不需要知道“折扣怎么算”,它只依赖一个抽象的 DiscountStrategy。想更换折扣规则,只要传入不同的策略实现即可,无需修改 Cart

单例模式:确保只有一个实例

单例模式(Singleton Pattern)用于保证某个类在应用生命周期内只有一个实例,并提供一个全局访问点。

它常用于管理共享资源,例如数据库连接、配置对象等:这类对象通常不希望被频繁创建,也不希望出现多个实例导致状态不一致。

/* by yours.tools - online tools website : yours.tools/zh/calorie.html */
<?php

class DatabaseConnection {
    private static ?DatabaseConnection $instance = null;

    // 私有化构造函数,防止外部 new
    private function __construct() {
        echo "连接到数据库...\n";
    }

    // 防止克隆
    private function __clone() {}

    // 防止反序列化
    public function __wakeup() {
        throw new \Exception("Cannot unserialize singleton");
    }

    public static function getInstance(): DatabaseConnection {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
}

// 使用示例
$db1 = DatabaseConnection::getInstance();
$db2 = DatabaseConnection::getInstance();

var_dump($db1 === $db2); // 输出: bool(true)

上面 DatabaseConnection 通过重写 __new__ 来确保无论实例化多少次,返回的都是同一个对象。

SOLID 原则

SOLID 是一组面向对象设计原则,目标是让系统更易维护、可扩展、可测试。把这些原则落到日常编码里,能减少很多常见的“面向对象写着写着就变成一团”的问题。

单一职责原则(SRP)

单一职责原则(Single Responsibility Principle)强调:一个类应该只有一个引起它变化的原因。换句话说,一个类最好只负责一件事。

例如 User 既保存用户数据又负责发送通知,会同时承担“数据模型”和“通知逻辑”两种职责。可以按 SRP 拆分:

<?php

class User {
    public function __construct(public string $name) {}
}

class UserNotification {
    public function sendWelcomeEmail(User $user): void {
        echo "正在向 {$user->name} 发送欢迎邮件。\n";
    }
}

// 使用
$user = new User("张三");
$notifier = new UserNotification();
$notifier->sendWelcomeEmail($user);

拆分之后,每个类的职责更集中,测试与修改也更可控。

开放/封闭原则(OCP)

开放/封闭原则(Open/Closed Principle)要求:对扩展开放,对修改封闭。也就是在不改动既有代码的前提下,能够通过扩展来引入新行为。

下面用形状面积示例说明:Shape 定义接口,不同形状通过继承实现 area(),调用方不需要知道具体类型。

<?php

interface Shape {
    public function area(): float;
}

class Circle implements Shape {
    public function __construct(private float $radius) {}
    public function area(): float {
        return pi() * pow($this->radius, 2);
    }
}

class Rectangle implements Shape {
    public function __construct(private float $width, private float $height) {}
    public function area(): float {
        return $this->width * $this->height;
    }
}

function printArea(Shape $shape) {
    echo "面积: " . $shape->area() . "\n";
}

// 使用
printArea(new Circle(5));
printArea(new Rectangle(4, 6));

当你要新增一种形状时,只需要添加新子类即可,不必改动 print_area 或既有形状代码。

里氏替换原则(LSP)

里氏替换原则(Liskov Substitution Principle)指出:基类能出现的地方,子类也应该能替换进去,并且不影响程序正确性。它要求子类是对父类能力的“真实扩展”,而不是“表面继承”。

下面这个例子里,Penguin 继承 Bird 但无法飞行,导致替换失败:

<?php

class Bird {
    public function fly(): void {
        echo "正在飞行...\n";
    }
}

class Penguin extends Bird {
    public function fly(): void {
        // 企鹅不会飞,违反了父类 Bird 的行为预期
        throw new Exception("企鹅不会飞!");
    }
}

/** * 修正方案:拆分接口
 * interface Bird {}
 * interface FlyingBird extends Bird { public function fly(); }
 * class Penguin implements Bird { ... }
 */

这个设计违反了 LSP:Penguin 无法在需要 Bird.fly() 的上下文中正常工作。要解决它,通常需要重新审视抽象(例如把“会飞”能力从 Bird 中拆出来,或者调整继承层次)。

接口隔离原则(ISP)

接口隔离原则(Interface Segregation Principle)强调:客户端不应该被迫依赖自己用不到的方法。与其做一个“很大很全”的接口,不如拆成多个小而聚焦的接口,让使用方按需组合。

<?php

interface Workable {
    public function work(): void;
}

interface Eatable {
    public function eat(): void;
}

class HumanWorker implements Workable, Eatable {
    public function work(): void { echo "人类在工作\n"; }
    public function eat(): void { echo "人类在吃饭\n"; }
}

class RobotWorker implements Workable {
    public function work(): void { echo "机器人在工作\n"; }
    // 机器人不需要实现 eat() 接口
}

这里 WorkerEater 拆成两个接口,避免了让所有实现者都背上不需要的职责。

依赖倒置原则(DIP)

依赖倒置原则(Dependency Inversion Principle)主张:高层模块不应该依赖低层模块,二者都应该依赖抽象(接口/抽象类)。这样可以降低耦合,让替换实现更容易。

<?php

interface Switchable {
    public function turnOn(): void;
    public function turnOff(): void;
}

class LightBulb implements Switchable {
    public function turnOn(): void { echo "灯亮了\n"; }
    public function turnOff(): void { echo "灯灭了\n"; }
}

class Switcher {
    // 依赖于接口,而不是具体的 LightBulb 类
    public function __construct(private Switchable $device) {}

    public function operate(): void {
        $this->device->turnOn();
    }
}

$bulb = new LightBulb();
$switch = new Switcher($bulb);
$switch->operate();

这里 Switch 直接依赖 LightBulb。如果把依赖改为抽象接口(比如 Switchable),Switch 就能支持更多设备实现,而不需要改动自身代码。

面向对象编程中的性能优化与内存考量

OOP 通常更强调清晰与可维护,但在大型系统里,性能与内存开销同样需要被考虑。理解对象创建、内存分配以及不可变性带来的影响,有助于在“可读性”和“效率”之间取得更好的平衡。

为值对象引入不可变性

PHP 8.2 引入了 readonly class,这非常适合实现不可变的值对象。不可变对象在处理值类型(Value Object)时很常见,例如 MoneyDateRangeCoordinate。把对象设计成不可变,可以减少共享状态带来的副作用,也更利于推理与并发场景下的安全性。

<?php

readonly class Money {
    public function __construct(
        public string $currency,
        public int $amount
    ) {}

    public function add(Money $other): Money {
        if ($this->currency !== $other->currency) {
            throw new InvalidArgumentException("币种不匹配");
        }
        // 返回新实例,而不是修改当前对象
        return new Money($this->currency, $this->amount + $other->amount);
    }
}

$m1 = new Money("CNY", 100);
$m2 = new Money("CNY", 50);
$result = $m1->add($m2);

echo $result->amount; // 150
// $m1->amount = 200; // 会报错,因为是 readonly

在这个例子里,Money 是不可变的:每次运算都会返回一个新实例,原对象保持不变。

避免对象抖动(Object Churn)

在紧密循环里频繁创建和销毁对象,会带来明显的性能问题:更多的内存分配、更高的垃圾回收(GC)压力。

遇到这种情况,可以考虑对象池(object pool)或复用对象,减少不必要的创建与回收开销。

总结:把原则与模式落到代码里

面向对象编程提供了一套组织复杂系统的工具。设计模式(例如 Strategy、Singleton)帮助你复用成熟的结构;SOLID 原则提醒你如何划分职责、控制依赖;而不可变对象、对象创建成本等性能视角则让系统在规模化场景下更稳。

OOP 的重点不在“有多少类”,而在抽象是否清晰、边界是否明确、实现是否可替换、变化是否被限制在可控的范围内。把这些要点坚持下来,代码通常会更好读,也更好维护。

STM32电机库无感代码注释无传感器版本龙贝格观测三电阻双AD采样前馈控制弱磁控制斜坡启动内容概要:本文档为一份关于STM32电机控制的无传感器版本代码注释资源,聚焦于龙贝格观测器在永磁同步电机(PMSM)无感控制中的应用。内容涵盖三电阻双通道AD采样技术、前馈控制、弱磁控制及斜坡启动等关键控制策略的实现方法,旨在通过详细的代码解析帮助开发者深入理解基于STM32平台的高性能电机控制算法设计工程实现。文档适用于从事电机控制开发的技术人员,重点解析了无位置传感器控制下的转子初始定位、速度估算系统稳定性优化等问题。; 适合人群:具备一定嵌入式开发基础,熟悉STM32平台及电机控制原理的工程师或研究人员,尤其适合从事无感FOC开发的中高级技术人员。; 使用场景及目标:①掌握龙贝格观测器在PMSM无感控制中的建模实现;②理解三电阻采样双AD同步采集的硬件匹配软件处理机制;③实现前馈补偿提升动态响应、弱磁扩速控制策略以及平稳斜坡启动过程;④为实际项目中调试和优化无感FOC系统提供代码参考和技术支持; 阅读建议:建议结合STM32电机控制硬件平台进行代码对照阅读实验验证,重点关注观测器设计、电流采样校准、PI参数整定及各控制模块之间的协同逻辑,建议配合示波器进行信号观测以加深对控制时序性能表现的理解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值