(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
SmartBear Collaborator - Concurrent User License (Includes 1 Year Maintenance)
VMware Fusion 10 Pro, ESD
IBM RATIONAL Quality Manager Quality Professional Authorized User Single Install License + Sw Subscription & Support 12 Months
erwin Data Modeler Workgroup Edition r9.7 - Product plus 1 Year Enterprise Maintenance Commercial
GFI FaxMaker и 1 год поддержки (10-49 лицензий)
 
Другие предложения...
 
Курсы обучения   WWW.ITSHOP.RU
 
Другие предложения...
 
Магазин сертификационных экзаменов   WWW.ITSHOP.RU
 
Другие предложения...
 
3D Принтеры | 3D Печать   WWW.ITSHOP.RU
 
Другие предложения...
 
Новости по теме
 
Рассылки Subscribe.ru
Информационные технологии: CASE, RAD, ERP, OLAP
Безопасность компьютерных сетей и защита информации
Новости ITShop.ru - ПО, книги, документация, курсы обучения
СУБД Oracle "с нуля"
OS Linux для начинающих. Новости + статьи + обзоры + ссылки
Все о PHP и даже больше
Мастерская программиста
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
 



    
rambler's top100 Rambler's Top100