Интернет, компьютеры, софт и прочий Hi-Tech

Подписаться через RSS2Email.ru

Краткое введение в трейты в PHP

Автор: andres, 14 января 2013

Идея трейтов довольно стара, так что до создания их PHP-версии было реализовано немало аналогичных подходов. Их истоки берут начало в 80-х, хотя это было и не совсем то, что мы сейчас называем трейтами. Эти подходы известны по таким документам, как Опыт применения трейтов в Xerox Star Workstation.

В последующие два десятилетия использовались различные близкие техники, такие как принципы языка Self и идея примесей. Позже, Группа программного обеспечения из Бернского университета усиленно разрабатывала идею трейтов, в результате чего возникла их первая реализация для Smalltalk. Она много раз была освещена на различных конференциях.

Мысль внедрить трейты в PHP родилась, похоже, в RFC за март 2008 года. Затем она была переосмыслена в новом RFC за октябрь того же года. Наконец, в марте 2012 года трейты увидели свет в релизе PHP 5.4.0.

Итак, что такое трейт?

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

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

Трейты могут решить эти проблемы, так как позволяют программисту группировать наборы поведений в новые концепции (трейты), которые могут потом «подключаться» к любым классам независимо от того, как они расположены в иерархии классов. Резюмируя, можно сказать, что трейт обладает следующими свойствами:

  • Он предоставляет готовое к использованию поведение в форме набора методов.
  • Трейт невозможно инстанцировать. Чтобы его использовать, его нужно включить в какой-нибудь класс, как его часть.
  • Чтобы предоставить какое-то специфическое поведение, трейт может нуждаться в содержащем его классе.

В некоторых реализациях (таких как Smalltalk) трейты являются простыми поведениями и не могут содержать внутреннего состояния. В других реализациях (таких как в PHP) — могут.

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

Модульная реализация слушателя

Определение трейта в PHP очень простое. Допустим, что в качестве примера мы хотим реализовать трейт Observable для модуляризации паттерна проектирования слушатель. Создав такой трейт мы сможем легко добавить поведение слушателя в любой класс с помощью одной простой строчки кода в нем. В качестве дополнительной выгоды мы сможем писать тесты, проверяющие работу трейта с большей уверенностью в том, что наш модуль работает как надо.

Перейдем к коду нашего примера. Настоящий простой слушатель может быть реализован так:

trait Observable
{
protected $subscribers = array();

public function onEventSendTo($event, $target, $message)
{
$action = function() use($message, $target){$target->$message();};
$this->subscribers[$event][] = $action;
}

public function trigger($event)
{
foreach ($this->subscribers[$event] as $function)
{
$function();
}
}
}

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

  • В PHP трейты могут определять состояния (инстанцируемая переменная $subscribers). Определенные в трейтах инстанцируемые переменные присоединяются к переменным целевого класса. В случае конфликта имен генерируется ошибка (смотрите раздел Свойства в Руководстве по трейтам в PHP).
  • Метод onEventSendTo() динамически создает функцию для посылки сообщения. Это делается с помощью PHP замыканий, о которых вы можете больше узнать из этого поста.
  • Метод trigger() перебирает заданные функции и выполняет их. Последнее делается в строке $function();.

Получив это определение, мы можем теперь написать несколько классов для выполнения первого теста. Сначала мы определим класс, который будет выполнять роль target реализуемого паттерна:

class TestTarget
{
use Observable;

...
}

Как вы можете заметить, новый протокол включается в класс действительно просто: вам всего лишь нужно добавить строку use Observable;. Удобно, что мы можем делать это на любом уровне иерархии классов, что делает трейты интересным способом горизонтального переиспользования кода.

Теперь мы создадим роль target, которая должна быть определена для observer-а:

class TestObserver
{
public function getNotification()
{
// Что-то делаем, когда происходит событие
}
}

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

class ObservableTest extends PHPUnit_Framework_TestCase
{
public function testTrigger()
{
// Готовим сценарий тестирования
$target = new TestTarget();
$observer = $this->getMock('TestObserver');

// Конфигурируем мок-объект соответственно ожидаемому поведению
$observer
->expects($this->once())
->method('getNotification');

// Конфигурируем коллбэк
$target->onEventSendTo('SomeEvent', $observer, 'getNotification');

// Активируем поведение
$target->trigger('SomeEvent');
}
}

Из этих тестов можно увидеть, что состояние и поведение, определенное в трейте Observable, теперь добавлено в класс TestTarget и может быть использовано так, как если бы описывалось в самом классе.

Некоторые вещи, которые стоит упомянуть

Я планировал сделать короткий пост, опустив некоторые важные концепции трейтов вообще и их реализации в PHP в частности:

  • Трейты предоставляют горизонтальные элементы поведения, которые могут быть очень хорошо структурированы. Это позволяет достичь их наилучшего переиспользования.
  • Трейты могут покрываться юнит-тестами, как какой-нибудь класс. В результате мы можем включать соответствующее поведение в класс уже с определенной степенью доверия.
  • Трейты в PHP могут определять внутреннее состояние. Это состояние объединяется с определением состояния целевого класса.

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

Автор: andres, 14.01.2013
Перевод с английского: Дмитрий Скоробогатов, специально для xBB.uz, 21.04.2014
Оригинальный текст может быть найден по адресу http://research.quanbit.com/2013/01/14/a-small-introduction-to-php-traits/


Предыдущие публикации:

Биржа долевых инвестиций SIMEX.

Последнее редактирование: 2014-04-21 06:21:44

Метки материала: трейты, программное обеспечение, php, введение в трейты, ооп, практика программирования, классы, трейты в php, class, trait

Оставьте, пожалуйста, свой комментарий к публикации

Представиться как     Антибот:
   

Просьба не постить мусор. Если вы хотите потестить xBB, воспользуйтесь кнопкой предварительного просмотра на панели инструментов xBBEditor-а.


© 2007-2017, Дмитрий Скоробогатов.
Разрешается воспроизводить, распространять и/или изменять материалы сайта
в соответствии с условиями GNU Free Documentation License,
версии 1.2 или любой более поздней версии, опубликованной FSF,
если только иное не указано в самих материалах.