Menu
CoddyTech

特征与继承

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

现在你已经了解了特征的工作方式,接下来我们来明确什么时候该使用特征,什么时候该使用继承。两者都能复用代码,但用途不同,特点也各不相同。

继承建立了“是一种”的关系。Dog 类扩展 Animal,表示狗是一种动物。子类继承父类的身份,并且可以在任何需要父类类型的地方使用。

特征提供“拥有某种功能”的能力。当一个类使用 Loggable 特征时,它就拥有日志记录功能,但它不会变成日志记录器。特征可以添加行为,而不改变类的本质。

<?php
// 继承:Dog 是 Animal
class Animal {
    public function breathe(): void {
        echo "Breathing";
    }
}

class Dog extends Animal {
    public function bark(): void {
        echo "Woof!";
    }
}

// Trait:Dog 具有日志记录功能
trait Loggable {
    public function log(string $msg): void {
        echo "Log: $msg";
    }
}

class Cat extends Animal {
    use Loggable;
}

需要记住的主要区别:

方面继承特征
关系是一种具有某种能力
限制只能有一个父类可以使用多个特征
类型检查适用于 instanceof没有类型标识
适用场景层级关系无关类之间共享行为

当类具有共同身份时,使用继承。当彼此无关的类需要相同的功能,但不属于同一个继承体系时,使用 trait。

challenge icon

挑战

简单

我们来构建一个车辆系统,展示何时该使用继承,何时该使用 trait。你将创建一个车辆层级结构,通过继承共享共同身份,同时通过 trait 添加横切功能。

你将把代码分为 4 个文件:

  • Trackable.php:创建一个名为 Trackable 的 trait,提供 GPS 跟踪功能。这个 trait 应包含:
    • 一个私有属性 $location(字符串)
    • 一个用于存储当前位置的方法 setLocation(string $location)
    • 一个返回 "Location: [location]" 的方法 getLocation()
    这个 trait 表示一种“拥有某项功能”的关系:任何车辆都可以拥有跟踪功能,但拥有跟踪功能并不意味着某个东西就是车辆。
  • Vehicle.php:创建一个基类 Vehicle,建立“属于某种类型”的关系。引入 trait 文件。使用构造函数属性提升定义一个受保护的 $brand 属性。添加一个返回 "Vehicle: [brand]" 的方法 describe()。由于我们车队中的所有车辆都会配备 GPS,这个类应使用 Trackable trait。
  • Car.php:创建一个继承自 Vehicle 的 Car 类。引入 Vehicle 文件。使用构造函数属性提升来接收品牌,并定义一个私有的 $doors(整数)属性。使用品牌调用父类构造函数。重写 describe() 方法,使其返回 "Car: [brand] with [doors] doors"。
  • main.php:引入 Car 文件。你会收到 3 个输入:品牌名称、车门数量和位置字符串。创建一个 Car 实例,设置它的位置,然后打印两行:
    • 调用 describe() 的结果
    • 调用 getLocation() 的结果

注意继承和 trait 在这里如何协同工作:Car 属于 Vehicle(继承确立身份),而 Vehicle 拥有 跟踪功能(trait 添加行为)。你可以使用 instanceof Vehicle 检查某个对象是否是车辆,但没有名为“Trackable”的类型可供检查:这正是这两种方式的关键区别。

亲手试试

<?php
// 主文件:创建并测试 Car 实例

// TODO:引入 Car.php 文件

// 读取输入
$brand = trim(fgets(STDIN));
$doors = intval(trim(fgets(STDIN)));
$location = trim(fgets(STDIN));

// TODO:使用 brand 和 doors 创建 Car 实例

// TODO:设置汽车的位置

// TODO:打印 describe() 的结果

// TODO:打印 getLocation() 的结果
?>
quiz icon自我测试

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

面向对象编程中的所有课

自己练习: PHP 在线编译器