Трейты и наследование
Часть раздела Объектно-ориентированное программирование путешествия по 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, когда несвязанным классам нужна одна и та же функциональность, но они не являются частью одного семейного дерева.
Задание
ЛегкоДавайте создадим систему транспортных средств, которая демонстрирует, когда следует использовать наследование, а когда — traits. Вы создадите иерархию транспортных средств, которые разделяют общую идентичность благодаря наследованию, добавляя при этом сквозные возможности с помощью traits.
Вы организуете код в четырёх файлах:
Trackable.php: создайте trait с именемTrackable, который предоставляет возможность GPS-трекинга. Trait должен содержать:- Приватное свойство
$location(строка) - Метод
setLocation(string $location), который сохраняет текущее местоположение - Метод
getLocation(), который возвращает"Location: [location]"
- Приватное свойство
Vehicle.php: создайте базовыйVehicleclass, устанавливающий отношение "is-a". Подключите файл trait. Используйте продвижение конструктора, чтобы определить защищённое свойство$brand. Добавьте методdescribe(), который возвращает"Vehicle: [brand]". Этот class должен использовать traitTrackable, поскольку все транспортные средства в нашем автопарке будут иметь GPS.Car.php: создайтеCarclass, который расширяетVehicle. Подключите файл Vehicle. Используйте продвижение конструктора, чтобы принять марку и приватное свойство$doors(целое число). Вызовите конструктор parent с маркой. Переопределите methoddescribe(), чтобы он возвращал"Car: [brand] with [doors] doors".main.php: подключите файл Car. Вы получите три входных значения: название марки, количество дверей и строку с местоположением. СоздайтеCarinstance, установите его местоположение, затем выведите две строки:- Результат вызова
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()
?>В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово $thisМетодыСвойстваКонструктор (__construct)Деструктор (__destruct)Повторение — простой калькулятор4Наследование
Основы наследованияКлючевое слово parent::Переопределение методовКлючевое слово finalАбстрактные классыПовторение — иерархия сотрудников7Инкапсуляция
Public, Protected, PrivateМодификаторы доступа подробноГеттеры и сеттерыСокрытие информацииПродвижение конструктора (8.0)Повторение — система учёта студентов2Пространства имён и автозагрузка
Введение в пространства имёнКлючевое слово useСтандарт автозагрузки PSR-4Автозагрузчик ComposerПовторение — структурированный проект5Интерфейсы и контракты
Введение в интерфейсыРеализация интерфейсовРеализация нескольких интерфейсовИнтерфейс и абстрактный классАннотация типов с интерфейсамиПовторение — калькулятор фигур8Магические методы
Введение в магические методы__toString и __debugInfo__get, __set, __isset, __unset__call и __callStatic__clone и клонирование объектов__serialize и __unserializeПовторение — пользовательская коллекция3Свойства классов
Свойства экземпляра и статические свойстваКонстанты в классахСтатические методы и свойстваПриватные и защищённые свойстваСвойства только для чтения (PHP 8.1)Повторение — менеджер банковского счёта6Полиморфизм
Повторение переопределения методовПолиморфизм через интерфейсыАннотации типов и объединённые типыПозднее статическое связываниеПовторение — обработчик платежей9Трейты
Введение в трейтыИспользование нескольких трейтовРазрешение конфликтов трейтовАбстрактные методы в трейтахТрейты и наследование12Паттерны проектирования, часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн StrategyПотренируйтесь самостоятельно: Онлайн-компилятор PHP