Menu
CoddyTech

解决特征冲突

本课属于 Coddy PHP 学习旅程的“面向对象编程”单元。第 53 课,共 91 课。

当两个 trait 定义了同名的方法时,PHP 会抛出致命错误。你必须使用 insteadof 和 as 运算符,明确告诉 PHP 如何处理这个冲突。

<?php
trait FileLogger {
    public function log(string $message): void {
        echo "File: $message";
    }
}

trait DatabaseLogger {
    public function log(string $message): void {
        echo "DB: $message";
    }
}

class Application {
    use FileLogger, DatabaseLogger {
        FileLogger::log insteadof DatabaseLogger;
        DatabaseLogger::log as logToDatabase;
    }
}

$app = new Application();
$app->log("Error occurred");
$app->logToDatabase("Error occurred");

输出:

File: Error occurred
DB: Error occurred

发生冲突时,insteadof 运算符会选择使用哪个 trait 的方法。在这个示例中,FileLogger::log 优先,因此会成为默认的 log() 方法。as 运算符会创建别名,让你可以用另一个名称访问另一个 trait 的方法。

你也可以使用 as 来更改方法的可见性:

<?php
trait Greeting {
    public function sayHello(): void {
        echo "Hello!";
    }
}

class Person {
    use Greeting {
        sayHello as private;
    }
}

这样一来,sayHello() 在 Person 类中就是私有的,尽管它在 trait 中是公开的。冲突解决让你可以精细地控制 trait 方法如何集成到类中。

challenge icon

挑战

简单

我们来构建一个通知系统,让不同的渠道都能发送消息,但当多个 trait 提供了同名方法时,我们需要解决冲突。

你需要将代码组织在 4 个文件中:

  • EmailNotifier.php:创建一个名为 EmailNotifier 的 trait,其中包含方法 notify(string $message),该方法返回 "Email: [message]"。
  • SmsNotifier.php:创建一个名为 SmsNotifier 的 trait,其中包含方法 notify(string $message),该方法返回 "SMS: [message]"。
  • AlertService.php:创建一个同时使用这两个 trait 的 AlertService 类。由于两个 trait 都有 notify() 方法,你需要解决这个冲突:
    • 使用 EmailNotifier::notify 作为默认的 notify() 方法
    • 为 SmsNotifier::notify 方法创建别名 notifyBySms
    此外,还要添加方法 notifyAll(string $message),调用这两个通知方法,并将它们的结果合并到不同的行中返回。
  • main.php:引入所有必需的文件,并创建一个 AlertService 实例。你会收到一个输入:消息字符串。输出 3 行:
    • 使用该消息调用 notify() 的结果
    • 使用该消息调用 notifyBySms() 的结果
    • 使用该消息调用 notifyAll() 的结果

这展示了当方法名称冲突时,insteadof 如何决定使用哪个 trait 的方法,而 as 则可以让你用另一个名称继续访问另一个 trait 的方法。

亲手试试

<?php
require_once 'AlertService.php';

// 读取输入
$message = trim(fgets(STDIN));

// TODO:创建一个 AlertService 实例

// TODO:传入消息调用 notify(),并打印结果

// TODO:传入消息调用 notifyBySms(),并打印结果

// TODO:传入消息调用 notifyAll(),并打印结果
?>
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: PHP 在线编译器