(495) 925-0049, ITShop интернет-магазин 229-0436, Учебный Центр 925-0049
  Главная страница Карта сайта Контакты
Поиск
Вход
Регистрация
Рассылки сайта
 
 
 
 
 

Типажи и анонимные функции в PHP. Кря-кря!

Источник: habrahabr
Deshene

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

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

Предисловие

Как-то так сложилось, что PHP (с появлением в нем ООП) в вопросах структуризации кода очень похож на Java. Наследуемся от класса, реализуем интерфейсы. Можем даже в параметрах методов указать необходимость принадлежности аргумента определенному генеалогическому древу. 

Но, если в Java, как в статически типизированном языке, это имеет смысл, так как позволяет выявить некоторый круг ошибок еще на этапе компиляции, то какой смысл во всех этих телодвижениях в динамически типизированном PHP? Может мы слишком много волнуемся о совсем ненужных вещах? Действительно ли нам так обязательно осведомляться о папах, бабушках или кузинах полученных нами объектов, когда нас в сущности интересует только то, может ли объект сделать то, что нам нужно?

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

Некоторое время назад в PHP появились анонимные функции (5.3), и я подумал: "Неплохо! Но не сильно полезно.". Потом (5.4) в PHP появились типажи, и я понял, что время пришло. Давайте наконец-то перейдем к примеру и посмотрим, что нам может предложить PHP.

Постановка задачи

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

Итак, какие действия можно производить над коллекциями. Ну, например, мы можем найти максимальный и минимальный элементы коллекции, или элементы удовлетворяющие определенному условию; можем получить новую коллекцию, применив над каждым элементом исходной коллекции какую-то операцию (map) и т.д… Набор этих методов прямо просится назвать типажом.

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

Каким образом мы можем реализовать этот контракт? Есть два варианта:

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

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

Реализация

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

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

Listing: Collections\Enumerable
namespace Collections; trait Enumerable { /** Контракт типажа Осуществляет обход коллекции, применяя $block к каждому ее элементу **/ abstract public function each(\Closure $block); public function findAll(\Closure $predicate) { $result = new FancyArray(); $this->each(function($el) use ($predicate, &$result) { if ($predicate($el)) { $result[] = $el; } }); return $result; } public function map(\Closure $block) { $result = new FancyArray(); $this->each(function($el) use ($block, &$result) { $processed = $block($el); $result[] = $processed; }); return $result; } /** Дальше следуют другие методы **/ }

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

Listing: Collections/FancyArray
namespace Collections; class FancyArray implements \ArrayAccess, \Countable { protected $container; function __construct($initial = array()) { if (is_array($initial)) { $this->container = $initial; } else if ($initial instanceof FancyArray) { $this->container = $initial->toArray(); } } public function offsetExists($offset) { return isset($this->container[$offset]); } public function offsetGet($offset) { return isset($this->container[$offset]) ? $this->container[$offset] : null; } public function offsetSet($offset, $value) { if (is_null($offset)) { $this->container[] = $value; } else { $this->container[$offset] = $value; } } public function offsetUnset($offset) { unset($this->container[$offset]); } public function toArray() { return $this->container; } public function count() { return count($this->container); } }

Теперь у нас есть массив, осталось лишь включить в него типаж Collections\Enumerable и реализовать контракт:
namespace Collections; class FancyArray implements \ArrayAccess, \Countable { use Enumerable; ... ... ... ... /** * Calls $block for every element of a collection * @param callable $block */ public function each(\Closure $block) { foreach ($this->container as $el) { $block($el); } } }
Как видите все довольно тривиально, зато теперь мы можем делать, например, такие штуки (и это потребовало от нас всего нескольких строчек кода):
$a = new FancyArray([1, 2, 3, 4]); $res = $a->map(function($el) { return $el*$el; }); // [1, 4, 9, 16] $res->reduce(0, function($initial, $el) { return $initial + $el; })); // 30
Это, конечно, занятно, но давайте пойдем дальше. Чем, файл, например не коллекция? Коллекция конечно, так что нам ничего не мешает сделать, например, так:
namespace IO; class FancyFile extends \SplFileObject { use \Collections\Enumerable; public function each(\Closure $block) { $this->fseek(0); while ($this->valid()) { $line = $this->fgets(); $block($line); } } }
Включили типаж, реализовали контракт, и теперь мы можем, например, подсчитать суммарную длину строк файла нечетной длины следующим образом (если нам это вообще когда-нибудь понадобиться ^_^ ):
$obj = new FancyFile(<filename>); $res = $obj ->select(function($el) { return strlen(trim($el)) % 2 == 1; }) ->map(function($el) { return strlen(trim($el)); }) ->reduce(0, function($initial, $el) { return $initial + $el; });
Вот такие вот дела, господа. 

Заключение

На мой взгляд, использование типажей (или сходных механизмов) более естественно в динамически типизированных языках, нежели танцы с интерфейсами и наследованием. Это дает нам большую гибкость и выразительность, а разве не именно за этим мы пришли сюда? Но у каждой медали есть обратная сторона, и тут этой обратной стороной может быть гораздо более сложный для восприятия код, более запутанный и неявный код. Помните, если что-то можно сделать, это необязательно нужно делать. Большая сила - большая ответственность, господа!

Ссылки по теме


 Распечатать »
 Правила публикации »
  Написать редактору 
 Рекомендовать » Дата публикации: 21.05.2013 
 

Магазин программного обеспечения   WWW.ITSHOP.RU
Zend Guard 1 Year Subscription
ABViewer Enterprise пользовательская
SAP Crystal Reports 2008 INTL WIN NUL License
Quest Software. TOAD for Oracle Edition
Quest Software. SQL Navigator for Oracle
 
Другие предложения...
 
Курсы обучения   WWW.ITSHOP.RU
 
Другие предложения...
 
Магазин сертификационных экзаменов   WWW.ITSHOP.RU
 
Другие предложения...
 
3D Принтеры | 3D Печать   WWW.ITSHOP.RU
 
Другие предложения...
 
Новости по теме
 
Рассылки Subscribe.ru
Информационные технологии: CASE, RAD, ERP, OLAP
Безопасность компьютерных сетей и защита информации
Новости ITShop.ru - ПО, книги, документация, курсы обучения
СУБД Oracle "с нуля"
OS Linux для начинающих. Новости + статьи + обзоры + ссылки
Программирование на Visual Basic/Visual Studio и ASP/ASP.NET
Adobe Photoshop: алхимия дизайна
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
 



    
rambler's top100 Rambler's Top100