Миксины Vs Делегирование: Преимущества И Недостатки При Реализации «Плагинов»

В этой статье я предлагаю вам свой взгляд на выбор использования миксинов или делегирования в проектах для добавления новой функциональности в класс.

Начальные условия следующие: мы рассматриваем миксины, имеющие собственное состояние и имеющие доступ ко всем членам класса-агрегатора.

Все публичные члены класса миксина становятся частью агрегатора.

Оставляем за кадром вопрос производительности.

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

Данная статья является в некотором роде логическим продолжением этот статьи о реализации миксинов в PHP. Если вам интересно, прочтите.

Если нет, то это не будет проблемой для понимания изложенного здесь материала.

Примеры кода Во-первых, давайте посмотрим на различия между двумя подходами, используя код, который делает одно и то же.



Примесь

  
   

class Aggregator { } class Mixin { public function doSomething() {} } Mixins::mix("Aggregator", "Mixin"); $a = new Aggregator (); $a->doSomething();



Делегация



Class Aggregator {} class Mixin { public function doSomething() {} } class AggregatorMixed extends Aggregator { private $mixin; function __construct() { $mixin = new Mixin(); } public function doSomething() { $mixin->doSomething(); } } $a = new AggregatorMixed (); $a->doSomething();

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

Но это не всегда возможно и я привел пример здесь в таком виде из-за рассмотрения миксинов и делегирования в контексте реализации плагинов ORM, которые вы найдете ниже.

Технические различия Чем эти два фрагмента отличаются с технической точки зрения? Как видите, они предоставляют одинаковую функциональность.

1. Методы миксина вызываются динамически по имени, и в случае делегирования компилятор может проверить перед выполнением кода.

Динамические вызовы придают программе гибкость за счет отсутствия проверки типов во время компиляции.

Если вы используете миксины, вам пригодятся дополнительные модульные тесты.

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

Создание и поддержка подкласса требует некоторых усилий, а объем собственного кода немного увеличивается, но в итоге вы видите полноценный класс, демонстрирующий все предоставляемые им возможности.

Миксины подключаются к классам очень быстро без какого-либо дополнительного кодирования, но в итоге вы видите агрегатный класс отдельно и все миксины отдельно.

Еще одним небольшим следствием является то, что в случае делегирования решение об объеме делегируемой функциональности принимает класс-агрегатор, в случае с миксинами — сам миксин.

3. Миксин автоматически имеет доступ ко всем членам класса-агрегатора и может произвольно манипулировать им, при условии, что знает, как он структурирован.

В случае делегирования этот доступ недоступен.

Доступ к членам класса-агрегатора из примеси дает нам возможность изменять внутреннее состояние объекта.

Это удобно, но добавляет в миксин необходимость знать внутреннюю структуру класса-агрегатора.

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

Однако эту привилегию можно легко отобрать у примеси.

Теперь давайте посмотрим, как те же возможности можно реализовать с помощью примесей и делегирования.

Пример «Поведение в ORM».

Мы сделали небольшой сайт, используя наш воображаемый фреймворк.

На этом сайте есть каталог статей.

Наша структура содержит ORM, который имеет модель «статьи».

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

Что мы делаем? Мы добавляем модель поведения «дерево» в класс модели «статья».

В этом случае мы можем выбрать либо миксин, либо делегирование.

Результаты будут напоминать примеры, которые мы рассматривали в самом начале.

Обратите внимание на одну вещь.

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

В следующей версии фреймворка нам нужно добавить в модель управление версиями и мягкое удаление.

Что мы легко делаем, либо добавляя в модель соответствующие миксины, либо расширяя класс модели с помощью делегирования.

Обратите внимание, что в этом примере можно использовать как миксины, так и делегирование.

Теперь рассмотрим аналогичный пример, только теперь мы добавляем не функциональность модели, а плагины к нашему фреймворку, которые пишут сторонние разработчики.

От нас требуется лишь предоставить удобный API, чтобы для интеграции плагина в систему нам не пришлось переписывать большую его часть.

Плагин хочет добавить функциональность нашей модели.

Если плагин построен на миксине, он просто регистрируется в классе модели.

А в случае делегирования мы могли бы, например, хранить имя класса модели в реестре.

Затем плагин добавления древовидной структуры в статьи наследует себя от базового класса модели фреймворка, добавляет функциональность и заменяет имя исходного класса модели в реестре модели на свое собственное.

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

Схема немного усложнилась, но пока терпимо.

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

Но их рассмотрение будет тема отдельной статьи).

Но авторы плагинов «версионирования» и «мягкого удаления» хотят, чтобы эти возможности были добавлены в модель статьи не «вместо», а «вместе»! Как это можно реализовать? В случае с примесями все по-прежнему просто.

Мы смешиваем все новые и новые функциональные возможности в классе модели.

А как насчет делегирования? Видимо, здесь схема начинает усложняться.

Нам нужно, чтобы классы плагинов наследовались друг от друга, а класс первого плагина наследовался от исходного класса модели.

Такая функциональность также может быть добавлена за определенную плату.

Что для вас предпочтительнее, вы должны решить сами.

В этом случае выбор следует делать исходя из своих профессиональных навыков и предпочтений.

В серьезном, необразовательном проекте следует использовать только ту технику, которая наиболее знакома и знакома вам профессионально (или та, которая наиболее знакома и знакома большинству программистов, которые будут работать над проектом в будущем).

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

Если вы чувствуете, что одинаково хорошо владеете миксинами и делегированием, соображения по выбору обоих приведены выше.

Решите, что вам больше нравится.

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

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

Мне жаль, если это так.

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

Я пытался обеспечить первое.

Второе зависит от вас.

P.S. Если эта статья показалась вам интересной, предложите тему для следующей в комментариях.

Теги: #ООП #php #mixin #делегирование #разработка сайтов

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.