Черти и анонимни функции в PHP. Квак Квак!

функции
В тази статия няма да говоря за това какво са Traits, няма да описвам синтаксиса или да анализирам всякакви тънкости, свързани с разрешаването на имена и наследяването на Traits.

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

Предговор

По някакъв начин се случи така, че PHP (с появата на OOP в него) по отношение на структурирането на кода е много подобен на Java. Ние наследяваме от класа и реализираме интерфейсите. Можем дори да посочим в параметрите на методите дали аргументът принадлежи на определено родословно дърво.

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

Лесно е да се види, че говоря за въвеждане на патици. Достатъчно мощна концепция, след която можете да напишете много изразителен код и не непременно склонен към повече грешки и по-малко стабилен от използването на класическия подход (можете да потърсите в Google достатъчно материали по тази тема, макар и главно по отношение на Ruby).

PHP въведе анонимни функции преди време (5.3) и аз си помислих: "Не е лошо! Но не е много полезно." Тогава (5.4) черти се появиха в PHP и разбрах, че е дошло времето. Нека накрая да преминем към пример и да видим какво може да предложи PHP.

Формулиране на проблема

Така че, нека се упражняваме с използване на черти с анонимни функции. На какво да тренираме? Е, колекциите ми дойдоха на ум, така че ще тренираме върху тях. Първо, нека помислим какво искаме и какви са начините да постигнем това.

И така, какви действия могат да се извършват върху колекции. Е, например, можем да намерим максималните и минималните елементи на колекция или елементи, които отговарят на определено условие; можем да получим нова колекция, като приложим някакъв вид операция (карта) към всеки елемент от оригиналната колекция и т.н. ... Наборът от тези методи е директно помолен да се назове Тип.

Какво ни трябва от колекцията, за да можем да приложим тези методи? Само едно: трябва да можем да прегледаме елементите на колекцията. Нека наречем това необходимата функционалност договор.

Как можем да изпълним този договор? Има две възможности:

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

И двата подхода са добре (може би първият е малко по-гъвкав от втория), но ще избера подход номер 2, защото, първо, той ще служи по-добре на целите ми, и второ, защото аз исках.

Изпълнение

И така, нека напишем нашия тип, водени от съображенията, описани по-горе.

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

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

Заключение

Според мен използването на черти (или подобни механизми) е по-естествено в динамично типизираните езици, отколкото танцуването с интерфейси и наследяване. Това ни дава много гъвкавост и изразителност и не затова ли дойдохме тук? Но всяка монета има обратна страна и тук тази обратна страна може да бъде много по-труден за разбиране код, по-объркващ и неявен код. Не забравяйте, че ако нещо може да се направи, не е задължително. Голяма сила - голяма отговорност, господа!