PHP 怎么PHP 委派

wen PHP项目 2

本文目录导读:

PHP 怎么PHP 委派

  1. 显式委派(最常用)
  2. 通过魔术方法 __call 实现动态委派
  3. 匿名函数 / Closure 委派
  4. 接口委派(组合优先于继承)
  5. PHP 8 的构造函数属性提升 + 委托
  6. 总结对比
  7. 实际开发建议

在 PHP 中,“委派”(Delegation)是一种设计模式,核心思想是一个对象将某些任务交给另一个对象去完成,而不是自己亲自实现,这可以降低耦合度、提高代码复用性,并让职责更加清晰。

PHP 本身没有像 Java 那样专门的 delegate 关键字,但可以通过以下几种方式实现委派模式。


显式委派(最常用)

一个对象持有另一个对象的引用,并在自己的方法中调用该对象的方法。

<?php
class Printer
{
    public function print(string $message): void
    {
        echo "[Printer]: $message\n";
    }
}
class Report
{
    private Printer $printer;
    public function __construct(Printer $printer)
    {
        $this->printer = $printer;
    }
    public function printReport(string $content): void
    {
        // 委派给 Printer 对象
        $this->printer->print($content);
    }
}
// 使用
$printer = new Printer();
$report  = new Report($printer);
$report->printReport("2024年度财务报告"); // 输出: [Printer]: 2024年度财务报告

优点:类型安全、IDE 智能提示好、代码清晰。
缺点:需要手动添加委派方法,如果委派的方法很多,代码会冗余。


通过魔术方法 __call 实现动态委派

适用于对象需要将多个方法的调用全部转发给另一个对象。

<?php
class Calculator
{
    public function add(int $a, int $b): int
    {
        return $a + $b;
    }
    public function multiply(int $a, int $b): int
    {
        return $a * $b;
    }
}
class CalculatorProxy
{
    private Calculator $calculator;
    public function __construct(Calculator $calculator)
    {
        $this->calculator = $calculator;
    }
    // 所有不存在的方法都会走这里
    public function __call(string $name, array $arguments): mixed
    {
        if (method_exists($this->calculator, $name)) {
            return $this->calculator->$name(...$arguments);
        }
        throw new \BadMethodCallException("Method $name not found.");
    }
}
// 使用
$proxy = new CalculatorProxy(new Calculator());
echo $proxy->add(2, 3);      // 5
echo $proxy->multiply(2, 3); // 6

优点:无需逐个写委派方法。
缺点:动态调用导致 IDE 无法静态分析、类型推断困难,性能稍低。


匿名函数 / Closure 委派

将可调用对象(闭包、函数)作为“委派目标”传递给另一个对象。

<?php
class TaskRunner
{
    private $delegate;
    // 接收任何可调用对象
    public function setDelegate(callable $delegate): void
    {
        $this->delegate = $delegate;
    }
    public function run(string $task): void
    {
        if ($this->delegate) {
            ($this->delegate)($task); // 执行委派
        }
    }
}
// 使用
$runner = new TaskRunner();
$runner->setDelegate(function (string $task) {
    echo "正在处理: $task\n";
});
$runner->run("备份数据库"); // 输出: 正在处理: 备份数据库

优点:非常灵活,无需定义新类。
缺点:难以进行类型约束,可读性依赖注释。


接口委派(组合优先于继承)

通过接口定义行为,将实现委派给具体的子类对象。

<?php
interface Logger
{
    public function log(string $message): void;
}
class FileLogger implements Logger
{
    public function log(string $message): void
    {
        file_put_contents('/tmp/log.txt', $message . PHP_EOL, FILE_APPEND);
    }
}
class UserService
{
    private Logger $logger;
    public function __construct(Logger $logger)
    {
        $this->logger = $logger;
    }
    public function register(string $email): void
    {
        // ... 注册逻辑 ...
        $this->logger->log("用户 $email 注册成功");
    }
}
// 使用
$service = new UserService(new FileLogger());
$service->register("test@example.com");

优点:符合 SOLID 原则,可以轻松更换不同的委派实现(如 FileLogger -> DatabaseLogger)。
缺点:需要定义接口和具体实现类,代码量稍多。


PHP 8 的构造函数属性提升 + 委托

PHP 8 可以更简洁地写出显式委派:

<?php
class NotificationService
{
    public function __construct(
        private EmailSender $emailSender,
        private SmsSender   $smsSender
    ) {}
    public function sendWelcomeEmail(string $email): void
    {
        $this->emailSender->send($email, "欢迎加入!");
    }
    public function sendAlertSms(string $phone): void
    {
        $this->smsSender->send($phone, "系统告警!");
    }
}

总结对比

方式 适用场景 类型安全 动态性 代码量
显式委派 固定、少量方法 ✅ 高 中等
__call 魔术方法 大量方法需全部转发 ❌ 低 较少
匿名函数/Closure 临时逻辑、策略模式变体
接口委派 职责分离、可替换实现 ✅ 高 较多

实际开发建议

  1. 优先使用显式委派或接口委派,代码可读性最好,IDE 支持最佳。
  2. 除非是框架层面的动态代理(如 Laravel 的 Facade、魔术方法),否则谨慎使用 __call
  3. 委派模式在 策略模式代理模式桥接模式 中非常常见,理解委派有助于掌握这些模式。

如果你有具体的使用场景(比如想实现类似装饰器、动态代理等功能),可以告诉我,我可以给出更针对性的代码示例。

抱歉,评论功能暂时关闭!