Menu
CoddyTech

组合与继承的对比

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

在上一章中,你学到 trait 提供“拥有”能力,而继承则建立“是”关系。组合进一步运用“拥有”这一概念,通过简单对象构建复杂对象,并将简单对象存储为属性,而不是继承它们。

通过继承,Car 可以扩展 Vehicle。通过组合,Car 包含 一个 Engine 对象:

<?php
class Engine {
    public function start(): string {
        return "Engine started";
    }
}

class Car {
    private Engine $engine;
    
    public function __construct() {
        $this->engine = new Engine();
    }
    
    public function start(): string {
        return $this->engine->start();
    }
}

$car = new Car();
echo $car->start();

输出:

Engine started

Car 类不继承自 Engine,而是拥有一个 Engine。这种方式有明显的优势:你可以换用另一种实现的引擎,类之间保持松散耦合,还能避免深层继承造成的僵化层级结构。

面向对象编程中的一条常见准则是“优先使用组合,而不是继承。”对于真正的类型层次结构,继承很适用(Dog 确实是 Animal),但如果要用独立的部分组装功能,组合就更有优势。你可以组合多个对象,在运行时替换它们,并单独测试各个组件。

下一课将介绍依赖注入。依赖注入建立在组合的基础上,通过控制对象获取依赖项的方式来实现。

challenge icon

挑战

简单

让我们使用组合来构建一个计算机系统,其中 Computer 由单独的组件对象组装而成,而不是通过继承这些组件来构建。这展示了组合如何让你用简单、独立的部分构建复杂对象。

你要把代码组织到 3 个文件中:

  • Processor.php:创建一个表示 CPU 的 Processor 类。使用构造函数属性提升来定义私有的 $model(字符串)和私有的 $cores(整数)。添加一个 getSpecs() 方法,返回 "[model] ([cores] cores)"。
  • Memory.php:创建一个表示 RAM 的 Memory 类。使用构造函数属性提升来定义私有的 $size(表示 GB 的整数)。添加一个 getCapacity() 方法,返回 "[size]GB RAM"。
  • main.php:引入这两个组件文件,并创建一个使用组合的 Computer 类。Computer 应该:
    • 将一个 Processor 对象和一个 Memory 对象存储为私有属性
    • 在构造函数中接收处理器型号、核心数和内存大小
    • 在构造过程中于内部创建组件对象
    • 包含一个 describe() 方法,返回 "Computer with [processor specs] and [memory capacity]"

    你会收到 3 项输入:处理器型号、核心数和以 GB 为单位的内存大小。使用这些规格创建一个 Computer,并打印调用 describe() 的结果。

注意,Computer 并没有继承 Processor 或 Memory。它是“包含”它们。通过组合建立的这种“拥有”关系,让每个组件保持独立且可复用,同时也允许 Computer 在需要时将任务委托给它的组成部分。

亲手试试

<?php
require_once 'Processor.php';
require_once 'Memory.php';

class Computer
{
    // TODO: 定义私有属性,用于存储 Processor 和 Memory 对象

    // TODO: 构造函数应接收处理器型号、核心数和内存大小
    // 并在内部创建组件对象
    public function __construct()
    {
        // TODO: 创建 Processor 和 Memory 对象并存储它们
    }

    // TODO: 实现 describe() 方法,使其返回
    // "Computer with [processor specs] and [memory capacity]"
    public function describe(): string
    {
        // TODO: 调用组件对象的方法并返回格式化后的字符串
    }
}

// 读取输入
$processorModel = trim(fgets(STDIN));
$cores = intval(fgets(STDIN));
$memorySize = intval(fgets(STDIN));

// TODO: 根据给定的规格创建 Computer 对象,并打印 describe() 的结果

?>
quiz icon自我测试

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

面向对象编程中的所有课

自己练习: PHP 在线编译器