Блог веб-разработчика v 1.0.0
Symfony2, AngularJS, React, Gulp, PhpStorm и много других страшных слов

Изучаем трейты

10 лет назад
16201 просмотр
PHP Перевод

Вольный перевод с английского статьи с сайта Coder on Code.

PHP все обрастает новыми функциями и постепенно исправляет свои прошлые ошибки. В PHP 5.4 одним из важных нововведений являются трейты (traits). PHP на протяжении всего времени поддерживал только единичное наследование классов, т.е. каждый класс мог иметь только одного родителя, что на практике иногда плохо влияло на организацию кода и могло привести к его дублированию.

Языки вроде C++ или Python решают эту проблему путем разрешения наследования от нескольких классов (мультинаследования). Ruby для этого использует примеси (Mixins). Независимо от техники проблема одна и та же. Трейты являются еще одним способом решения этой проблемы и используются, например, в таких языках как Perl и Scala.

Хотя PHP 5.4 (и трейты) доступны примерно с начала 2012 года, многие разработчики могут быть не знакомы с концепцией и возможностями, которые дают нам трейты. В этой статье я хочу рассказать о трейтах, их использовании, преимуществах и недостатках.

PHP и множественное наследование

Основная причина того, что в PHP небыло множественного наследования это "проблема ромба" (diamond problem), которая возникает при определенном мультинаследовани. "Проблема ромба" - это неоднозначность которая возникает при мультинаследованиии.

Приведу пример. Классы B и C наследуют класс A, а класс D наследует одновмеременно B и C. В классе A есть метод, который переопределяется одновременно в классах B и C, но не переопределяется в классе D. Какой метод должен наследовать класс D?

Допустим, что в PHP есть множественное наследование (на самом деле нет), тогда "проблема ромба" в наших следующих примерах будет примерно такой: класс не будет знать какой метод roar() наследовать.

Это, по сути, проблема мультинаследования и PHP, к счастью (а может наоборот), поддерживает только единичное наследование и не сможет создать подобную ошибку. Но с версии PHP 5.4 мы все же сможем реализовать подобный функционал.

Особенности трейтов

Трейты - это механизм для повторного использования кода в языках с единичным наследованием (как PHP). Трейты избавляют разработчика от некоторых ограничений единичного наследования, позволяя использовать наборы методов независимо в различных классах. php.net.

Самый простой путь понять трейты: думать о них как об интерфейсах с реализацией методов. Как мы уже говорили, единичное наследование было частью ООП в PHP на протяжении многих лет и многие разработчики были разочарованы этой частью, когда пытались написать чистый код для крупного проекта.

С помощью трейтов мы можем использовать функционал других классов (прим. пер., на самом деле не классов, а именно трейтов) и при этом не должны наследовать их. Это звучит как мультинаследование, но на самом деле трейты больше похожи на "горизонтальное переиспользование кода", в том время как наследование является "вертикальным переиспользованием кода".

Как мы говорили раньше, трейты похожи на абстрактные класс, например, нельзя создать объект напрямую из трейта. Давайте посмотрим пример:

<?php
 
class Animal {
   public function roar() {
      echo "Makes animal sounds";
   }
}
 
trait Cat {
   public function roar() {
      echo "Meowww";
   }
}
 
class Tiger extends Animal {
   use Cat;
}
 
$tiger = new Tiger;
$tiger->roar();

Если вы думаете, что смогли бы сделать то же самое единичным наследованием путем создания класса Cat, который наследует Animal, а затем наследовать Tiger от Cat, то попробуйте сделать следующее:

<?php
 
class Animal {
   public function roar() {
      echo "Makes animal sounds";
   }
}
 
trait Cat {
   public function roar() {
      echo "Meowww";
   }
}
 
trait BigCat {
   public function kill() {
      echo "Kill an animal";
   }
}
 
class Tiger extends Animal {
   use Cat;
   use BigCat;
}
 
$tiger = new Tiger;
$tiger->roar();
$tiger->kill();

Круто, правда? Попробуйте сделать так же без мультинаследования.

Лучшее в трейтах - это их структурированность. Cat и BigCat имеют много общего (те же кошки, но большие), однако очень удобно разбить методы по разным трейтам и использовать их по необходимости. На самом деле, мы могли бы пойти еще дальше:

<?php
 
class Animal {
   public function roar() {
      echo "Makes animal sounds";
   }
}
 
trait Feline {
   public function roar() {
      echo "Meowww";
   }
}
 
trait BigCat {
   public function kill() {
      echo "Kill an animal";
   }
}
 
class Tiger extends Animal {
   use Feline;
   use BigCat;
}
 
class Cat extends Animal {
   use Feline;
}
 
// Cat class
 
$cat = new Cat;
$cat->roar();
 
// Tiger class
 
$tiger = new Tiger;
$tiger->roar();
$tiger->kill();

Выводы

Трейты - это невероятное дополнение языка PHP и мы рассмотрели их только поверхностно. В будущих статьях расскажем о более углубленном использовании трейтов.

Оригинальная статья: http://coderoncode.com/2014/03/17/exploring-traits.html
Что еще почитать