Трейты — PHP: Погружаясь в классы
Трейты — альтернативный механизм переиспользования общего кода в разных классах. Он устраняет ограничения, которыми обладает наследование и заменяет его.
Трейты похожи на абстрактные классы. Они реализуют какую-то общую функциональность и с ними нельзя работать напрямую. Единственный способ использовать их – включение в другие классы.
Трейт по большей части выглядит как (абстрактный) класс и устроен как класс. Он подчиняется тем же правилам именования и расположения в иерархии пространств имён (а следовательно и файловой структуре) что и классы. Отличия начинаются в момент использования:
Трейт включается в другой класс с помощью инструкции use . С этого момента в классе становится доступна вся функциональность, определённая в трейте. По поведению трейты похожи на наследование, например, приватные части трейта доступны только внутри методов самого трейта. Но при этом трейт не встраивается в цепочку наследования, это легко проверить:
Из этого есть пара важных следствий:
- Внутри класса к методам трейта нельзя обратиться через parent , только через $this . При условии, что эти методы не приватные.
- Трейт не может реализовывать интерфейс. Это могут делать только классы.
Зачем?
Трейты в отличие от наследования, не фиксируют структуру классов. Любой класс может включать в себя любое количество трейтов:
Эта структура располагает к выделению общих признаков из совершенно разнообразных классов. Пример с Magic как раз хорошо демонстрирует такой подход. Многие классы одинаково реализуют магические методы и нет смысла дублировать их код. И точно не стоит использовать наследование, так как оно свяжет совершенно несвязанные классы в общую (и жёсткую) иерархию.
Трейты позволяют реализовать многие интерфейсы PHP универсальным образом, например, ArrayAccess или Iterator
Пример: Итератор
Рассмотрим готовый пример трейта-итератора. Он реализует общую логику обхода коллекций внутри объектов. С его помощью можно сделать объекты, которые можно использовать в forEach :
Обратите внимание на важную деталь — трейт получает саму коллекцию. Трейт требует от класса, который его включает, реализации метода getCollection() (помните, что трейт похож на абстрактный класс, он может определять абстрактные методы). Этот метод используется внутри трейта для доступа к элементам коллекции, по которой он итерирует.
Это очень важная концепция. Трейту нужны данные от класса, в который его включают. И трейт строит связь с этими классами через интерфейсный метод, а не через обращение к свойству с конкретным именем. А вот класс от трейта ничего не требует. Благодаря тому, что связь строится в одну сторону (трейт зависит от метода класса, но класс не зависит от методов и свойств трейта), код остаётся модульным. Если бы и трейт требовал что-то от класса и класс от трейта, то почти наверняка в коде проблемы с архитектурой.
Открыть доступ
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно.
Зачем использовать iterator_traits в функции шаблона вместо того, чтобы просто использовать другой аргумент типа шаблона?
Ссылаясь, например, на этот фрагмент из cplusplus.com :
Вопрос в том, зачем использовать iterator_traits в этом контексте, вместо того, чтобы принимать другой аргумент шаблона, как показано здесь:
Решение
Предложение, которое вы предложили в комментариях — функция должна принимать другой аргумент шаблона — не будет работать так, как вы предполагали. Вот код, который вы предложили:
Проблема с этим кодом в том, что он больше не компилируется:
Проблема здесь заключается в том, что для вызова шаблона функции каждый аргумент шаблона должен быть либо выводимым из типов аргументов, либо явно указанным вызывающей стороной. Здесь тип DiffType не может быть выведено из типов аргументов ( InputIterator а также T типы могут быть выведены из двух аргументов, но только из сигнатуры нет контекста), поэтому этот вызов завершится с ошибкой компилятора. Использование std::iterator_traits Вот шаблон общего шаблона для извлечения информации об итераторе из самого типа итератора.
Структура iterator_traits
Вспомогательная структура-шаблон, используется для указания всех критических определений типов, которые должен иметь итератор.
Синтаксис
Remarks
Структура-шаблон определяет типы элементов
iterator_category : синоним для Iterator::iterator_category .
value_type : синоним для Iterator::value_type .
difference_type : синоним для Iterator::difference_type .
distance_type : синоним для Iterator::difference_type.
pointer : синоним для Iterator::pointer .
reference : синоним для Iterator::reference .
Частичные специализации определяют критические типы, связанные с указателем объекта типа type* или const Type*.
В этой реализации вы также можете применить несколько шаблонов функций, которые не используют частичную специализацию:
что определяет некоторые из тех же типов более косвенно. Используйте эти функции как аргументы для вызова функции. Их единственной целью является предоставление полезного параметра шаблона класса вызываемой функции.