Menu
Coddy logo textTech

Трейты и наследование

Часть раздела Объектно-ориентированное программирование путешествия по PHP на Coddy. Урок 55 из 91.

Теперь, когда вы понимаете, как работают traits, давайте уточним, когда использовать traits, а когда — наследование. И то и другое позволяет повторно использовать код, но они служат разным целям и обладают различными характеристиками.

Inheritance устанавливает отношение «is-a». Dog class, расширяющий Animal, означает, что собака является животным. Дочерний class наследует идентичность parent и может использоваться везде, где ожидается тип parent.

Трейты предоставляют возможность «has-a». Когда класс использует трейт Loggable, он has функциональность ведения журнала — но не становится логгером. Трейты добавляют поведение, не изменяя сущность класса.

<?php
// Наследование: Dog IS an Animal
class Animal {
    public function breathe(): void {
        echo "Breathing";
    }
}

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

// Трейт: Dog HAS возможность логирования
trait Loggable {
    public function log(string $msg): void {
        echo "Log: $msg";
    }
}

class Cat extends Animal {
    use Loggable;
}

Ключевые различия, которые следует помнить:

АспектInheritanceТрейты
Связьявляетсяимеет capability
ОграничениеТолько один родительРазрешено несколько трейтов
Проверка типаРаботает с instanceofНет идентичности типа
Лучше всего подходит дляИерархических связейОбщего поведения в несвязанных классах

Используйте Inheritance, когда классы имеют общую идентичность. Используйте traits, когда несвязанным классам нужна одна и та же функциональность, но они не являются частью одного семейного дерева.

challenge icon

Задание

Легко

Давайте создадим систему транспортных средств, которая демонстрирует, когда следует использовать наследование, а когда — traits. Вы создадите иерархию транспортных средств, которые разделяют общую идентичность благодаря наследованию, добавляя при этом сквозные возможности с помощью traits.

Вы организуете код в четырёх файлах:

  • Trackable.php: создайте trait с именем Trackable, который предоставляет возможность GPS-трекинга. Trait должен содержать:
    • Приватное свойство $location (строка)
    • Метод setLocation(string $location), который сохраняет текущее местоположение
    • Метод getLocation(), который возвращает "Location: [location]"
    Этот trait представляет возможность "has-a": любое транспортное средство может иметь отслеживание, но наличие отслеживания не делает нечто транспортным средством.
  • Vehicle.php: создайте базовый Vehicle class, устанавливающий отношение "is-a". Подключите файл trait. Используйте продвижение конструктора, чтобы определить защищённое свойство $brand. Добавьте метод describe(), который возвращает "Vehicle: [brand]". Этот class должен использовать trait Trackable, поскольку все транспортные средства в нашем автопарке будут иметь GPS.
  • Car.php: создайте Car class, который расширяет Vehicle. Подключите файл Vehicle. Используйте продвижение конструктора, чтобы принять марку и приватное свойство $doors (целое число). Вызовите конструктор parent с маркой. Переопределите method describe(), чтобы он возвращал "Car: [brand] with [doors] doors".
  • main.php: подключите файл Car. Вы получите три входных значения: название марки, количество дверей и строку с местоположением. Создайте Car instance, установите его местоположение, затем выведите две строки:
    • Результат вызова describe()
    • Результат вызова getLocation()

Обратите внимание, как здесь работают вместе наследование и traits: Car is a Vehicle (наследование устанавливает идентичность), а Vehicle has возможность отслеживания (trait добавляет поведение). Вы могли бы использовать instanceof Vehicle, чтобы проверить, является ли нечто транспортным средством, но не существует типа "Trackable", с которым можно было бы сравнивать: в этом заключается ключевое различие между этими двумя подходами.

Попробуйте сами

<?php
// Основной файл — создание и тестирование экземпляра Car

// TODO: Подключите файл Car.php

// Чтение входных данных
$brand = trim(fgets(STDIN));
$doors = intval(trim(fgets(STDIN)));
$location = trim(fgets(STDIN));

// TODO: Создайте экземпляр Car с brand и doors

// TODO: Установите location автомобиля

// TODO: Выведите результат describe()

// TODO: Выведите результат getLocation()
?>
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор PHP