PHP: Разница между Трейтом, Интерфейсом и Абстрактным классом
Если вы постоянно работаете с PHP, то скорее всего сталкивались с Интерфейсом, Трейтом или Абстрактным классом. На первый взгляд может показаться, что между ними есть некоторое сходство, и трудно определить их различия и варианты использования. К концу этой статьи вы легко сможете сказать, что отличает их друг от друга и когда предпочтительнее использовать один или другой.
TL;DR;
- Абстрактный класс может содержать сигнатуры методов, а также общие методы, но не может быть создан сам по себе. Хорошо подходит для создания общего родителя для совместного использования между классами.
- Трейт — группа свойств и методов для повторного использования кода, и в один класс можно добавить несколько. Хорошо подходит для организации кода и уменьшения повторений.
- Интерфейс — набор сигнатур методов для обеспечения конкретной реализации в классе, к которому они добавляются. Хорошо подходит для добавления структуры и стандартизации.
Хотите узнать больше о каждом из них? Читайте дальше!
Абстрактный класс
Абстрактный класс по структуре похож на обычный класс, но содержит абстрактные методы. В отличие от обычного класса не может быть создан сам по себе. Является хорошей отправной точкой для классов, которые, скорее всего, будут иметь общего родителя.
Допустим у нас были классы Cat
, Dog
и Hamster
. У них могут быть общие методы и функциональные возможности, поэтому можно создать родительский абстрактный класс как для добавления общих методов, так и для обеспечения необходимой реализации в дочерних классах.
Родительский абстрактный класс для них может выглядеть так:
<?php
abstract class Pet
{
abstract protected function greet();
public function hasFur()
{
return true;
}
}
У нас есть абстрактный метод greet()
, который заставляет любой класс расширяющий Pet
реализовать этот метод. Затем у нас есть публичный метод hasFur()
, который будет доступен для любого объекта, созданного из класса, расширяющего этот абстрактный класс.
Итак, теперь мы можем создать класс для определённого вида домашних животных:
<?php
class Cat extends Pet
{
public function greet()
{
return 'Meow!';
}
}
$cat = new Cat();
$cat->greet(); // Meow!
$cat->hasFur(); // true
Выводы об абстрактных классах:
- Не могут быть созданы сами по себе
- Абстрактные методы просто объявляют сигнатуру (без функциональности)
- Используются дочерними классами с расширениями
- Действует как своего рода частично построенный класс
Трейт
Трейты — способ повторного использования кода в нескольких (иногда совершенно несвязанных между собой) классах. В отличие от абстрактных классов, в одном классе можно использовать несколько трейтов с помощью оператора use
.
Трейты регулярно используют для группировки нескольких связанных между собой методов и свойств, добавляя эту функциональность к классам, в которых они используются.
Например, у нас может быть два трейта HasLegs
и HasFins
:
<?php
trait HasLegs
{
public function walk()
{
$steps = 0;
while (true) {
$steps++;
}
}
}
<?php
trait HasFins
{
public function swim()
{
$laps = 0;
while (true) {
$laps++;
}
}
}
Мы продолжаем работать с животными из предыдущих примеров, так что давайте представим, что у нас есть объект Cat
, у которого явно есть ноги и он может ходить. Мы можем использовать трейт HasLegs
, чтобы дать нашему классу Cat
метод walk()
, который можно использовать:
<?php
class Cat
{
use HasLegs;
}
$cat = new Cat();
$cat->walk();
Или, что у нас есть объект Duck
, у которого есть ноги и плавники? Мы можем использовать оба трейта и дать нашему классу методы walk()
и swim()
:
<?php
class Duck
{
use HasLegs, HasFins;
}
$duck = new Duck();
$duck->walk();
//или
$duck->swim();
Выводы о трейтах:
- Идеально подходит для повторного использования кода
- Реализуется в теле класса с
use
- В одном классе можно использовать несколько
- Может иметь как свойства так и методы
Интерфейс
Интерфейсы — способ принудительного применения определённых реализаций в классах, которые их используют. Интерфейсы могут содержать только сигнатуры методов, в них вообще нет никакой функциональности. В основном они предназначены для обеспечения структуры и служат чертежами для построения классов.
Возьмём этот пример интерфейса:
<?php
interface Bug
{
public function legs();
public function eyes();
}
Опять, никакой реализации методов legs()
и eyes()
, только сигнатура, которую они требуют в классах включающих этот интерфейс. Итак, если мы создаём классы, реализующие наш интерфейс Bug
, эти метода должны быть доступны в каждом из них.
<?php
class Spider implements Bug
{
public function legs()
{
return 8;
}
public function eyes()
{
return 8;
}
}
<?php
class Beetle implements Bug
{
public function legs()
{
return 6;
}
public function eyes()
{
return 2;
}
}
Итак, теперь у нас есть два класса, каждый из которых реализует интерфейс Bug
, каждый из них должен включать методы legs()
и eyes()
, а также собственную (обычно уникальную) реализацию.
Выводы об интерфейсах:
- Действуют как чертежи для классов
- Используются в объявлении класса с реализациями
- Можно использовать несколько интерфейсов в одном классе
- Содержит только сигнатуры методов (без свойств)
Надеюсь теперь вы лучше понимаете различия и сходства между абстрактными классами, трейтами и интерфейсами. Каждый из них мощный и при различных сочетаниях может добавить много мощности и структуры вашим PHP-приложениям.
Рекомендую прочитать статьи Интерфейсы vs Абстрактные классы в PHP и PHP: Когда использовать трейт?:
- PHP: Используем Интерфейсы для улучшения кода
- PHP: Интерфейсы vs Абстрактные классы
- PHP: Когда использовать трейт