Статьи

Признания преобразованного разработчика PHP: смешивание как пекарь

Одна вещь, которая всегда заставляла меня содрогаться в 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--&gt;
Check out my awesome &lt;?PHP echo $this-&gt;linkFor('http://malstaxidermy.com', 'Stuffed Mongooses'); ?&gt;.
</code>

Это с радостью создаст ссылку на мой сайт с чучелами мангустов.

И толпа сходит с ума …

Это довольно распространенная вещь, и хотя некоторые из вас могут пялиться на свои экраны, крича «Почему вы просто не создали абстрактный класс GenericMixin, а затем расширились от этого?» Я хотел бы начать со слов «Вау, добрый сэр! У меня есть точка! »

Хотя есть много причин, по которым это не идеальное решение, я просто остановлюсь на двух.

  1. 90% времени я не контролирую объекты, которые расширяю. Платформа PHP, которую мы используем, состоит из нескольких подмодулей, которые работают причудливым и замечательным (читай: раздражающим) образом, что создает головные боли при редактировании корневого класса. В качестве альтернативы это может быть библиотека, которую мы используем, и, как правило, вы хотите избегать редактирования внешних зависимостей, если это возможно.
  2. Что если я захочу добавить другой функционал, который часто используется? Отбросить реализацию кэширования на лету / объект? PHP не обрабатывает множественное наследование, поэтому на данный момент мы вернулись к первому пункту — поместили код непосредственно в наши классы или создали какой-то нелепый базовый абстрактный класс с как можно большим количеством реализаций шаблона.

В чем ваша точка зрения? Я здесь для Руби!

Хорошая новость заключается в том, что, как и во многих языках, в Ruby встроена поддержка миксов. Это так же просто, как определить модуль, а затем включить его в определение нашего класса. Оператор includeinclude В то время как в PHP это будет включать файл, то, что мы делаем в Ruby — это добавление всех методов из модуля в класс.

Давайте воссоздадим нашу функциональность HtmlView в Ruby.

 module TagViewHelper
	def tag_for(url, title)
		"<a href='%s'&gt;%s&lt;/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.