Одна вещь, которая всегда заставляла меня содрогаться в PHP, заключалась в том, что у меня не было возможности легко реализовать универсальные миксины классов. Возможность избежать многократного написания кода важна для меня как для разработчика, так как она делает меня более эффективным в моей работе, поэтому я могу выполнять более важный код за то же время или больше расслабляться, сохраняя тот же вывод кода (это в основном зависит от моего настроения в любое время).
Если бы у меня был доллар за каждый раз, когда я должен был написать набор функций, которые я знал, что написал раньше, но также знал, что не было никакого способа избежать, я бы… ну… не очень много, но по крайней мере достаточно, чтобы покрыть ночь. Смирись со мной в этом, есть немало PHP-кода. (Для записи я обычно выискивал функции и копировал, вставлял их.)
Вот класс View с базовым набором функций, которые позволяют мне реализовать что-то похожее на шаблон дизайна mixin .
class HtmlView {
protected $_helpers = array();
public function addHelper($obj)
{
array_push($this->_helpers, $obj);
return $this;
}
public function __call($name, $params)
{
foreach($this->_helpers as $helper)
{
if(is_callable(array($helper, $name )))
return call_user_func_array(array($helper, $name), $params);
}
throw new Exception('Attempted to call nonexistent method ' . $name . ' on ' . __CLASS__);
}
}
Теперь я могу создать вспомогательный класс с кучей методов, к которым я хочу иметь доступ из моего HtmlView.
class TagViewHelper{
public function linkFor($url, $name)
{
return sprintf('<a href="%s">%s', $url, $name);
}
# the rest of my bunch of methods here...
}
Я мог бы либо добавить TagViewHelper в мой класс HtmlView при создании экземпляра, либо вставить его в конструктор.
public function __construct()
{
$this->addHelper(new TagViewHelper);
# Add other view helpers here
}
Теперь я могу вызывать методы из вспомогательного класса из экземпляра объекта HtmlView.
<!-- somewhere deep inside code being called by our HtmlView object-->
Check out my awesome <?PHP echo $this->linkFor('http://malstaxidermy.com', 'Stuffed Mongooses'); ?>.
</code>
Это с радостью создаст ссылку на мой сайт с чучелами мангустов.
И толпа сходит с ума …
Это довольно распространенная вещь, и хотя некоторые из вас могут пялиться на свои экраны, крича «Почему вы просто не создали абстрактный класс GenericMixin, а затем расширились от этого?» Я хотел бы начать со слов «Вау, добрый сэр! У меня есть точка! »
Хотя есть много причин, по которым это не идеальное решение, я просто остановлюсь на двух.
- 90% времени я не контролирую объекты, которые расширяю. Платформа PHP, которую мы используем, состоит из нескольких подмодулей, которые работают причудливым и замечательным (читай: раздражающим) образом, что создает головные боли при редактировании корневого класса. В качестве альтернативы это может быть библиотека, которую мы используем, и, как правило, вы хотите избегать редактирования внешних зависимостей, если это возможно.
- Что если я захочу добавить другой функционал, который часто используется? Отбросить реализацию кэширования на лету / объект? PHP не обрабатывает множественное наследование, поэтому на данный момент мы вернулись к первому пункту — поместили код непосредственно в наши классы или создали какой-то нелепый базовый абстрактный класс с как можно большим количеством реализаций шаблона.
В чем ваша точка зрения? Я здесь для Руби!
Хорошая новость заключается в том, что, как и во многих языках, в Ruby встроена поддержка миксов. Это так же просто, как определить модуль, а затем включить его в определение нашего класса. Оператор include
include
В то время как в PHP это будет включать файл, то, что мы делаем в Ruby — это добавление всех методов из модуля в класс.
Давайте воссоздадим нашу функциональность HtmlView в Ruby.
module TagViewHelper
def tag_for(url, title)
"<a href='%s'>%s</a>" % [ url, title ]
end
end
class HtmlView
include TagViewHelper
end
Теперь я могу вызывать tag_for(“http://malstaxidermy.com”, “Solid Snakes”)
Ни в коем случае мне не приходилось беспокоиться об обработке Mixins, потому что это обрабатывается Ruby.
Вы должны признать, что это намного меньше кода. Модули Mixin являются одной из замечательных возможностей Ruby и позволяют нам объединять общее поведение в набор общих объектов.
Вам может быть интересно, что произойдет, если у вас есть несколько методов с одинаковым именем. Ruby справляется с этим, просто перезаписывая все предыдущие методы последним добавленным.
У вас есть какие-нибудь истории или советы по миксинам на PHP или Ruby?
Примечание. Это очень простой пример, разработанный для того, чтобы показать, как воссоздание некоторой функциональности, требующей значительного количества кода в PHP, может быть очень быстро и легко обработано с помощью Ruby.