Если вы регулярно работаете с PHP, скорее всего, вы сталкивались с интерфейсом, трейтом или абстрактным классом. На первый взгляд может показаться, что между ними есть несколько общих черт, и бывает трудно разобраться в их различиях и вариантах использования. К концу этой статьи вы сможете легко определить, что их отличает друг от друга, и когда лучше использовать один из них, а когда другой.
tl;dr
- Абстрактный класс может содержать сигнатуры методов, а также общие методы, но не может быть инстанцирован сам по себе. Хорошо подходит для создания общего родителя для обмена между классами.
- Trait — это группа свойств и методов для повторного использования кода, их можно добавить несколько в один класс. Хорошо подходит для организации и уменьшения количества повторений.
- Интерфейс — это набор сигнатур методов для обеспечения определенной реализации в классе, к которому они добавляются. Хорошо подходит для добавления структуры и стандартизации.
Хотите узнать больше о каждом из них? Читайте дальше!
Абстрактный класс
Абстрактный класс структурирован так же, как и обычный класс, но содержит абстрактные методы. В отличие от обычного класса, он не может быть создан сам по себе. Они являются хорошей отправной точкой для классов, которые, скорее всего, имеют общего родителя.
Допустим, у нас есть классы 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
Некоторые выводы об абстрактных классах:
- Не могут быть инстанцированы сами по себе
- Абстрактные методы просто объявляют сигнатуру (без функциональности).
- Используются дочерним классом с помощью
extends
. - Действуют как своего рода частично встроенный класс
Трейт
Трейты — это способ повторного использования кода в нескольких (иногда совершенно несвязанных) классах. В отличие от абстрактных классов, несколько трейтов могут быть использованы в одном классе с помощью оператора 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();
//or
$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()
, а также их собственную (обычно уникальную) реализацию.
Некоторые выводы об интерфейсах:
- Действуют как чертежи для классов
- Используются в объявлении класса с помощью
implements
. - В одном классе можно использовать несколько интерфейсов
- Могут содержать только подписи методов (без свойств).
Завершение
Надеюсь, теперь вы лучше понимаете различия и сходства между абстрактными классами, трейтами и интерфейсами. Каждый из них является мощным инструментом, и, комбинируя их различными способами, вы можете добавить много силы и структуры в ваши PHP-приложения.
Если у вас есть какие-либо вопросы по этой теме или по любой другой, связанной с веб-разработкой, задавайте их в комментариях или пишите мне в Twitter!