The difference between Traits, Interfaces, and Abstract Classes in PHP
If you’ve been working with PHP regularly, chances are you’ve run across an Interface, Trait, or Abstract Class. At first glance, they might appear to have a few similarities between them, and it can be hard to make out their differences and use cases. By the end of this article, you should be able to easily tell what sets them apart, and when it’s best to use one over the other.
tl;dr
- An Abstract Class can contain method signatures as well as common methods, but can’t be instantiated on its own. Good for creating a common parent to share between classes.
- A Trait is a group of properties and methods for code re-use, and multiple can be added to a single class. Good for organization and reducing repetition.
- An Interface is a set of method signatures to enforce a particular implementation in the class they’re added to. Good for adding structure and standardization.
Want to learn more about each of these? Keep reading!
Abstract Class
An abstract class is structured a lot like a normal class, but contains abstract methods as well. Unlike a normal class though, it can’t be instantiated by itself. They make good starting points for classes that would likely share a common parent.
Let’s say that we had Cat, Dog, and Hamster classes. These might share some common methods and functionality, so a parent abstract class could be created to both add shared methods as well as enforce required implementations in the child classes.
A parent abstract class for those might look something like this:
We have an abstract method greet() which will force any class extending Pet to implement that method. Then we have a public hasFur() method which will be accessible to any object created from a class that extends this abstract class.
So now we can create a class for a particular kind of pet:
> $cat = new Cat(); $cat->greet(); // Meow! $cat->hasFur(); // true
Some takeaways about abstract classes:
- Can’t be instantiated on their own
- Abstract methods just declare a signature (no functionality)
- Used by a child class with extends
- Act as a sort of partially-built class
Trait
Traits are a way to re-use code in multiple (and sometimes completely unrelated) classes. Unlike abstract classes, multiple traits can be used on a single class with the use statement.
They’re regularly reached for to group together somewhat related methods and properties, adding that functionality to classes they’re used in.
For example, we might have two traits HasLegs and HasFins :
We’re continuing with the animal examples from earlier, so let’s say that we had a Cat object that clearly has legs and can walk. We can use our HasLegs trait to give our Cat class a walk() method that can be used:
Or, what if we had a Duck object, that kind of has both legs and fins? We can use both traits and give our class walk() and swim() methods:
$duck = new Duck(); $duck->walk(); //or $duck->swim();
Some takeaways about traits:
- Perfect for code reuse
- Implemented in the class body with use
- Multiple can be used in the same class
- Can have both properties and methods
Interface
Interfaces are a way to enforce specific implementations in classes that use them. Interfaces can only contain method signatures, there’s zero functionality in them at all. They’re mostly there to ensure structure and act as blueprints for building classes.
Let’s take this example of an interface:
Again, there’s no implementation of the legs() and eyes() methods, just the signature that they’re required in classes that include this interface. So, if we create classes that implement our Bug interface, those methods need to be available in each of them.
So now we have two classes each implementing our Bug interface, which are both required to include the legs() and eyes() methods as well as their own (usually unique) implementation.
Some takeaways about interfaces:
- Act as blueprints for classes
- Used in the class declaration with implements
- Multiple can be used in the same class
- Can only contain method signatures (no properties)
Wrap Up
Hopefully now you have a better understanding of the distinctions and similarities between Abstract Classes, Traits, and Interfaces. Each of them are powerful and when combined in different ways can add a lot of power and structure to your PHP applications.
If you have any questions about this, or anything else related to web development, feel free to let me know in the comments or reach out to me on Twitter!
My Newsletter
Subscribe using the form below and about 1-2 times a month you’ll receive an email containing helpful hints, new packages, and interesting articles I’ve found on Laravel, Vue, Docker, and more.
PHP: Разница между Трейтом, Интерфейсом и Абстрактным классом
Эти три структуры могут сбить с толку новичков в PHP или опытных разработчиков, поэтому давайте рассмотрим, что делает каждая из них и когда их лучше всего использовать.
Если вы постоянно работаете с PHP, то скорее всего сталкивались с Интерфейсом, Трейтом или Абстрактным классом. На первый взгляд может показаться, что между ними есть некоторое сходство, и трудно определить их различия и варианты использования. К концу этой статьи вы легко сможете сказать, что отличает их друг от друга и когда предпочтительнее использовать один или другой.
- Абстрактный класс может содержать сигнатуры методов, а также общие методы, но не может быть создан сам по себе. Хорошо подходит для создания общего родителя для совместного использования между классами.
- Трейт — группа свойств и методов для повторного использования кода, и в один класс можно добавить несколько. Хорошо подходит для организации кода и уменьшения повторений.
- Интерфейс — набор сигнатур методов для обеспечения конкретной реализации в классе, к которому они добавляются. Хорошо подходит для добавления структуры и стандартизации.
Хотите узнать больше о каждом из них? Читайте дальше!
Абстрактный класс
Абстрактный класс по структуре похож на обычный класс, но содержит абстрактные методы. В отличие от обычного класса не может быть создан сам по себе. Является хорошей отправной точкой для классов, которые, скорее всего, будут иметь общего родителя.
Допустим у нас были классы Cat , Dog и Hamster . У них могут быть общие методы и функциональные возможности, поэтому можно создать родительский абстрактный класс как для добавления общих методов, так и для обеспечения необходимой реализации в дочерних классах.
Родительский абстрактный класс для них может выглядеть так:
abstract class Pet
abstract protected function greet();
public function hasFur()
return true;
>
>
У нас есть абстрактный метод greet() , который заставляет любой класс расширяющий Pet реализовать этот метод. Затем у нас есть публичный метод hasFur() , который будет доступен для любого объекта, созданного из класса, расширяющего этот абстрактный класс.
Итак, теперь мы можем создать класс для определённого вида домашних животных:
class Cat extends Pet
public function greet()
return 'Meow!';
>
>
$cat = new Cat();
$cat->greet(); // Meow!
$cat->hasFur(); // true
Выводы об абстрактных классах:
- Не могут быть созданы сами по себе
- Абстрактные методы просто объявляют сигнатуру (без функциональности)
- Используются дочерними классами с расширениями
- Действует как своего рода частично построенный класс
Трейт
Трейты — способ повторного использования кода в нескольких (иногда совершенно несвязанных между собой) классах. В отличие от абстрактных классов, в одном классе можно использовать несколько трейтов с помощью оператора use .
Трейты регулярно используют для группировки нескольких связанных между собой методов и свойств, добавляя эту функциональность к классам, в которых они используются.
Например, у нас может быть два трейта HasLegs и HasFins :
trait HasLegs
public function walk()
$steps = 0;
while (true)
$steps++;
>
>
>
trait HasFins
public function swim()
$laps = 0;
while (true)
$laps++;
>
>
>
Мы продолжаем работать с животными из предыдущих примеров, так что давайте представим, что у нас есть объект Cat , у которого явно есть ноги и он может ходить. Мы можем использовать трейт HasLegs , чтобы дать нашему классу Cat метод walk() , который можно использовать:
class Cat
use HasLegs;
>
$cat = new Cat();
$cat->walk();
Или, что у нас есть объект Duck , у которого есть ноги и плавники? Мы можем использовать оба трейта и дать нашему классу методы walk() и swim() :
class Duck
use HasLegs, HasFins;
>
$duck = new Duck();
$duck->walk();
//или
$duck->swim();
- Идеально подходит для повторного использования кода
- Реализуется в теле класса с use
- В одном классе можно использовать несколько
- Может иметь как свойства так и методы
Интерфейс
Интерфейсы — способ принудительного применения определённых реализаций в классах, которые их используют. Интерфейсы могут содержать только сигнатуры методов, в них вообще нет никакой функциональности. В основном они предназначены для обеспечения структуры и служат чертежами для построения классов.
Возьмём этот пример интерфейса:
interface Bug
public function legs();
public function eyes();
>
Опять, никакой реализации методов legs() и eyes() , только сигнатура, которую они требуют в классах включающих этот интерфейс. Итак, если мы создаём классы, реализующие наш интерфейс Bug , эти метода должны быть доступны в каждом из них.
class Spider implements Bug
public function legs()
return 8;
>
public function eyes()
return 8;
>
>
class Beetle implements Bug
public function legs()
return 6;
>
public function eyes()
return 2;
>
>
Итак, теперь у нас есть два класса, каждый из которых реализует интерфейс Bug , каждый из них должен включать методы legs() и eyes() , а также собственную (обычно уникальную) реализацию.
- Действуют как чертежи для классов
- Используются в объявлении класса с реализациями
- Можно использовать несколько интерфейсов в одном классе
- Содержит только сигнатуры методов (без свойств)
Надеюсь теперь вы лучше понимаете различия и сходства между абстрактными классами, трейтами и интерфейсами. Каждый из них мощный и при различных сочетаниях может добавить много мощности и структуры вашим PHP-приложениям.
Важно убедится, что ваш код легко читается, поддерживается, расширяется и тестируется. Один из способов улучшить эти факторы — использовать интерфейсы.
Я решил написать статью, объясняющую различия между абстрактными классами и интерфейсами в PHP и дать краткий обзор, когда нужно использовать каждый из них.
Интерфейсы и трейты в PHP
Интерфейсы (interface) — это класс, в котором все методы являются абстрактными (abstract) и открытыми (public). В интерфейсе ООП PHP можно задать только имена методов и их параметры, а реализованы они обязаны быть в расширяемых ими классах.
Для реализации интерфейса мы используем ключевое слово implements. Класс может реализовать более одного интерфейса, которые разделяются запятыми. Как и классы, интерфейс может содержать константы, которые запрещено переопределять.
Трейты в ООП PHP
Трейты (trait) — это механизм обеспечения повторного использования кода в PHP с учетом одиночного наследования. Трейт отчасти реализует множественное наследование, позволяя разработчику повторно использовать наборы методов свободно, в нескольких независимых классах.
Трейт похож на класс, но создать экземпляр трейта невозможно. Он предназначен для группирования функционала, который потом используется в разных классах с помощью директивы use. Нужные для использования в классе трейты можно указать через запятую.
interface NameA < const CON=5*10;//50 public function getA(); public function setA($a); > interface NameB < public function getB(); public function setB($b); >//Объединяем NameA и NameB в Name interface Name extends NameA, NameB <> //Трейт - похож на класс trait My1 < public function summa($a,$b):float< return $a+$b; >> trait My2 < public function multi($a,$b):float< return $a*$b; >> //class Gena implements NameA, NameB class Gena implements Name < use My1, My2;//Подключаем Трейты (множественное наследование) private $a=1; private $b=2; public function getA()< return $this->a; > public function getB()< return $this->b; > public function setA($a)< $this->a=$a; > public function setB($b)< $this->b=$b; > > $objG=new Gena; //Содаём обект класса Gena $objG->setA(10); //Меняем значение $a $objG->setB(20); //Меняем значение $b echo $objG->getA()."
\n"; //10 - новое значение echo $objG->getB()."
\n"; //20 - новое значение echo $objG->summa(30,40)."
\n";//70 (30+40) echo $objG->multi(30,40)."
\n";//1200 (30*40) echo Gena::CON."
\n"; //50 - константа ?>