PHP: Разница между Трейтом, Интерфейсом и Абстрактным классом

Источник: «The difference between Traits, Interfaces, and Abstract Classes in PHP»
Эти три структуры могут сбить с толку новичков в 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();

Выводы о трейтах:

Интерфейс

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

Возьмём этот пример интерфейса:

<?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: Когда использовать трейт?:

Дополнительные материалы

Предыдущая Статья

JavaScript: различие между require и import

Следующая Статья

Laravel: Правило валидации с периодом дат и несколькими полями