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();

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

  • Идеально подходит для повторного использования кода
  • Реализуется в теле класса с 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: Когда использовать трейт?:

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

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

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

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

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