特征中的抽象方法
本课属于 Coddy PHP 学习旅程的“面向对象编程”单元。第 54 课,共 91 课。
特征可以提供开箱即用的方法,但有时你需要使用该特征的类来实现特定功能。这时,特征中的抽象方法就很有用,它们定义了一份使用该特征的类必须履行的约定。
当一个 trait 声明了抽象方法时,使用该 trait 的任何类都必须实现它:
<?php
trait Notifiable {
abstract public function getEmail(): string;
public function sendNotification(string $message): void {
echo "Sending to " . $this->getEmail() . ": $message";
}
}
class User {
use Notifiable;
public function __construct(private string $email) {}
public function getEmail(): string {
return $this->email;
}
}
$user = new User("john@example.com");
$user->sendNotification("Welcome!");
输出:
Sending to john@example.com: Welcome!这个 trait 提供了 sendNotification() 方法,但依赖类来实现 getEmail()。这样就形成了一种强大的模式:trait 处理通用逻辑,同时将类特有的细节交给实现它的类。
如果类使用了 trait,却没有实现抽象方法,PHP 就会抛出致命错误。这样可以确保 trait 安全地依赖某些方法的存在,让你的代码更可预测、更易维护。
挑战
简单让我们构建一个定价系统,让不同类型的产品以不同方式计算最终价格,同时通过包含抽象方法的 trait 共享折扣逻辑。
你要将代码组织到 3 个文件中:
Discountable.php:创建一个名为Discountable的 trait,用于提供折扣功能。这个 trait 应包含:- 一个抽象方法
getBasePrice(): float,使用此 trait 的类必须实现这个方法 - 一个方法
applyDiscount(int $percent),用于计算并返回对基础价格应用折扣百分比后的价格 - 一个方法
getFinalPrice(int $percent),用于返回格式化后的字符串:"Final price: $[discounted_price]",保留 2 位小数
- 一个抽象方法
Product.php:创建一个使用Discountabletrait 的Product类。引入 trait 文件。使用构造函数属性提升来定义一个私有的$name(字符串)和一个私有的$price(浮点数)。实现所需的getBasePrice()方法,返回产品的价格。main.php:引入 Product 文件。你会收到 3 个输入:产品名称、价格和折扣百分比。创建一个Product实例,并打印两行:- 格式化后的基础价格:
"Base: $[price]",保留 2 位小数 - 调用
getFinalPrice()并传入折扣百分比后得到的结果
- 格式化后的基础价格:
这种模式很实用,因为任何类都可以使用 Discountable trait,例如产品、服务和订阅,只要它们实现了 getBasePrice()。trait 提供共享的折扣逻辑,而每个类负责定义基础价格的来源。
亲手试试
<?php
require_once 'Product.php';
// 读取输入
$name = trim(fgets(STDIN));
$price = floatval(trim(fgets(STDIN)));
$discountPercent = intval(trim(fgets(STDIN)));
// TODO:用名称和价格创建一个 Product 实例
// TODO:按 "Base: $[price]" 的格式输出原价,保留 2 位小数
// TODO:传入折扣百分比调用 getFinalPrice(),并输出结果
?>这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: PHP 在线编译器