Как стать автором
Обновить

Комментарии 51

Мне нравится ваш стиль изложения.
Спасибо! Это действительно очень приятно слышать :)
Кстати, может кому-то будет полезно:
Я долго искал, как вызвать анонимное замыкание. function(){}() в php, ясен пень, не работает. Оказывается, можно сделать так:
$list = call_user_function(function($var) use ($lib) {
}, $myvar + 1);
Извините, а можно пару примеров, зачем вам это делать?
Для удобства. Например, на ходу преобразовать список, когда array_ функции не подходят (включая array_walk). Да много применений.

А зачем в javascript по-вашему так делают (например):
(function($){
}(jQuery));? (кстати, пример неудачный, но где-то близко)
Это делают чтобы не засорять глобальную область видимости. А jQuery и $ там стоят для того, чтобы не было конфликта имён (если правильно помню). И то, и то нецелесообразно, в PHP есть неймспейсы, классы и функции, и анонимные функции.

Можно пример поудачнее?
В JS не стоит лазить в глобальное пространство имён, так как на сайте скорее всего будут использоваться компоненты третьих лиц и могут возникнуть конфликты. Анонимные замыкания там используются лишь потому, что язык не поддерживает пространства имён.

В пхп анонимные замыкания не имеют смысла.
В php аномнимные замыкания можно использовать как раз для того, чтобы не засорять локальную область видимости.
Если у вас локальная область настолько разрослась, то вам стоит лучше продумать структуру проекта. Это же сколько нужно напихать всего в один метод, чтобы начались конфликты?
Само собой, нужно.

Но когда нужно срочно что-то поправить, на рефакторинг время нет. Анонимные функции позволяют делать быстрые правки не засоряя проект лапшекодом. Собственно, вы разве не согласны, что это не так уж и плохо? Лучше люди будут использовать замыкания, чем пихать все в длиннющий скрипт.
Лучше пусть они пишут правильно, времени на создание отдельного метода и времени на костыли в виде «напихать кучу замыканий в метод» (как звучит то!) примерно одинакового, это лишь стимулирование дополнительного говнокода, нежели действительно необходимая фича.
Например, если бы вы хотели получить список только публичных переменных класса прямо из метода класса «в одну строку» без использования Reflection и тому подобной тяжелой артиллерии, вы могли пользоваться таким трюком в PHP 5.3.
Ключевое слово «в одну строку»?
«В одну строку» я намеренно написал в кавычках, как вы можете видеть. Ключевое слово здесь «без использования Reflection». Например, в PHP 5.3 можно было сделать так:

call_user_func(function($object) {return get_object_vars($object);}, $this);


В 5.4 такой трюк не работает и остаётся только:

$reflection = new ReflectionObject($this);
foreach ($reflection->getProperties(ReflectionProperty::IS_PUBLIC) as $property) { //...

Картинка к посту намекает куда лучше с этим переходить? :)
Я постарался сделать все как можно менее навязчиво :)
На мой взгляд, использование типажей (или сходных механизмов) более естественно в динамически типизированных языках, нежели танцы с интерфейсами и наследованием.
Теперь осталось написать с десяток книг про ТОП(типажи-ориентированное-программирование), и подождать с десяток лет пока созреют хоть какие-то стандарты проектирования в этом стиле. А сама концепция мне чем-то напоминает инженерию генных модификаций: с одной стороны может быть очень круто, с другой стороны очень опасно.
Ну, в принципе то, о чем я написал это не Rocket Science, и на откровение не претендует. Похожая концепция уже давно есть в Ruby, только называется по-другому (модули). Правда, на мой взгляд, у типажей перед модулями есть одно преимущество — можно явно объявить контракт.
Касательно стандартов проектирования это конечно вопрос открытый, но хорошие примеры использования можно найти в Ruby on Rails.
Вообще-то это называется примесью, и этому стилю уже более 30 лет.
Не вижу противоречий. Просто в Ruby примеси это, грубо говоря, модули; в PHP — это типажи. Однако примесь, это на мой взгляд, более широкое понятие, т.е., типажи это примеси, но не все примеси — типажи. Я же хотел привести наиболее конкретное и практичное сравнение.
НЛО прилетело и опубликовало эту надпись здесь
Если подойти к вопросу строго, то трейты в PHP не охватывают полностью понятие «примесь». Трейт PHP это, скажем так, статическая примесь, которая применяется по отношению к классу. Но мы также можем реализовать механизм «подмешивания» по отношению к объектам, т.е. в рантайме, и тут тоже мы имеем дело с примесью, но это уже не будет трейт.
В целом, конечно, да: и трейты, и примеси, и модули — это об одном и том же, однако есть нюансы.
На русской википедии про трейты вообще бред написан.
За чем же дело стало?
Что нам нужно от коллекции для того, чтобы мы могли реализовать эти методы? Только одно: мы должны иметь возможность итерироваться по элементам коллекции. Давайте назовем этот необходимый функционал контрактом.

А давайте не будем!
Ну, можно и не называть. Однако называть все же удобнее, тогда текст читать проще ;) Если Вас конкретно слово «контракт» не устраивает, предложите более подходящую альтернативу.
Давайте я не буду городить огород и Вы тоже, просто потому что для подобных вещей в программировании существует одно и ровно одно слово — интерфейс(я не про тот который с ключевым словом в php). А синонимы Вы ему подбирайте какие Вам нравятся — соглашение об использовании, контракт, реализация методов и т.п.
Не собираююсь ничего навязывать и доказывать, просто заметил что слово «контракт» и правда режет немного слух, хотя ничего критичного в его использовании нет.
Возможно я немного запутанно написал, чем мог ввести в заблуждение. Так что постараюсь прояснить этот момент.
Интерфейс, по-сути, это публичная морда класса, или подсистемы, т.е., грубо говоря, набор методов, с которыми взаимодействует внешняя среда.
Термин "контракт" же я применяю по отношению к функционалу, который должен быть реализован в классе для того, чтобы иметь возможность включить определенный трейт, т.е. необходимый для его правильного функционирования.
Тут существует принципиальная разница! Несложно придумать пример когда пересечение интерфейса и контракта будет пусто.
Интерфейс описывает синтаксис, а контракт понятие более широкое, оно описывает и поведение как вызываемого, так и вызывающего кода.
Так то интересно, но постарайтесь брать более «живые» примеры.
Раньше из букв P,H,P пытались выкладывать слово Java, нынче выкладывают слово Scala.
Неплохо! :) Хотя, мне кажется, скорее не Scala, а Groovy.
В Scala, кстати, traits сделаны существенно грамотней (благодаря наличию нормальной теоретической основы, например, линеаризации базовых типов). Там это полезный и продуманный инструмент, а не просто перетянутая модная фишечка. И авторы языка предлагают несколько действительно полезных применений: предоставление «толстых» интерфейсов без дубликации кода (на примере стандартного класса Comparable [ala Boost.Operators] и стандартных коллекций), реализация декораторов (очень удобная и полезная на практике), механизм compile-time dependency injection, также известный как Cake-pattern.
Занятно! Слышал много хорошего (как и немного плохого) о Scala, но никак руки не доходили.

А касательно «модной фишечки», слышал в одном докладе занимательную аналогию: PHP — язык-пират, он крадет у других языков и присваивает себе. И смешно и грустно.
Неправда, все что крадет PHP у других языков — на самом деле он не крадет. На самом деле php что-то RFC'шит, потом пару лет тупит — опрометчивые товарищи к тому моменту как прочухается это уже реализуют, вот и создается видимость…
переложите в слове PHP 15 спичек так чтобы получилось… Вы об этом чтоли? Странно не это, странно то что многие еще и не справляются :)
Господи, причем тут интерфейсы? Трейты и миксины решают проблему множественного наследия.
решают? O RLY? Они грязно насилуют множественное наследование своим стеком объявлений вот и всё… Но ни разу не решают. Решали бы — в трейтах бы не было чудо-конструкций «а дайвайте сделает кроме s/p/p аксессоров еще и чудо-слова чтобы трейт типа перекрывал другой трейт»
Каким образом ваше мнение относится к моему комментарию в контексте поста?
Не соглашусь с вами. Между трейтами и интерфейсами довольно тесная связь. Одна из довольно часто используемых возможностей трейтов — как раз предоставление «толстого» интерфейса, не требуя от программиста, использующего трейт, определения большого числа методов (в то время как простой интерфейс требует определения всех методов, в нём объявленных). Собственно, в статье автор использует как раз эту идиому и описывает одну миллиардную часть реализации Scala-коллекций. В них достаточно примиксовать Traversable и определить один метод foreach, остальные 100500 полезных методов получаются автоматом.
Да, это не единственная ситуация, когда треты полезны. Тем не менее, за PHP не скажу, а уж в Scala трейты «проблему» множественного наследованя точно не решают. Например, в Scala у трейтов не может быть конструкторов.
Интерфейсы помогают описать контракт взаимодействия, а трейты принести стандратную реализацию для интефейсов.
К примеру можно взять пары из ZF2
ServiceLocatorAwareInterface && ServiceLocatorAwareTrait
EventManagerAwareInterface && EventManagerAwareTrait
LoggerAwareInterface && LoggerAwareTrait
Единственный вопрос — как бы это дело подружить с IDE? В случае интерфейсов и классов — подсказки методов работают отлично. Но что если нам в коде нужен функционал двух и более типажей?
Я использую IntelliJ Idea с PHP плагином, так вот он отлично дружит с типажами.
Вы не совсем поняли проблему. Допустим есть
trait Foo {
     public function f();
}
trait Bar {
    public function g();
}
class A {
    use Foo;
    use Bar;
}

И есть некая функция вида
function x($obj) {
    $obj->f();
    $obj->g();
}

Вот каким образом в функции x IDE сможет понять, что у obj есть методы f и g. Если бы описание функции было вида
function x(A $obj) {

то все бы работало отлично. Но в случае с утиной типизацией — мы не можем указывать тип принимаемого объекта в функции
Впринципе подумал, что с помощью phpDoc это сделать, например
/**
 * @param Foo|Bar $obj
 */
function x($obj) {
}

Но имхо это усложнит восприятие и не совсем верно с точки зрения семантики
Понял Вас. Да, к сожалению, такая проблема имеется. У себя решаю phpDoc'ами, но точечено. Более адекватного решения на вскидку предложить не могу.
Статься интересная, но все же:

1. Зачем использовать трейты для таких элементарных задач, я все же не понял.
2. Вы объявляете функции с жестко заданым параметром \Closure. Это работает, но в контексте проектирования это не верно.

public function each($callback)
{
  if (!is_callable($callback)) { throw new \InvalidArgumentException(); }
  // ....
}

Так Вы сможете тыкать туда не только анонимные, но и типа: usort, array($object, 'methodName')

Ну и автору на заметку:
1. Использования в коде PHP 5.4 на данный момент не рекомендуеться, так как множество проектов просто не смогут заработать на ПХП 5.3 (К примеру: объявление массива [], использования трейтов).
2. Для получения массива с объекта, Вы используете toArray. Лучше унаследуйтесь от интерфейса IteratorAggregate

P.S.
В плане реализации этой задачи можно сразу использовать \Doctrine\Common\Collections\Collection :D
Использование PHP 5.4 для того чтобы объявлять массивы вот так: [] не рекомендуется, а если уж нужны трейты, то лучше выбрать 5.4, а не отказываться от такого удобного инструмента.
Да, я с Вами вполне согласен! Я тоже иногда в проектах использую трейты, чтобы сократить время, но вот была на днях засада: Создал компонент, используя трейты, а в заказчика на хостинге PHP 5.3. Ну и на хостингах не везде можно попросить установить ПХП 5.4 (К примеру Mirohost). Вот и облажался.

Да и вот какие есть реальные задачи, где нужно использовать трейты (если говорить о проектировании системы)?
Почти все проблемы решаються выбором той или инной схемы патерна.
Облажались вы в том, что не согласовали версию с заказчиком :) Написали бы на 5.3, а у него 5.2 бы оказался.

В PHP в последнее время много нововведений, являющихся лишь синтаксическим сахаром, без ввода новых парадигм, но отсутствие которых раздражает. Трейты — одно из них.
2.
public function each(callable $callback)
{
  // ...
}


1. На носу выход 5.5 и, видимо, прекращение поддержки 5.3. Не, конечно, если делать движок, максимально совместимый со всем чем можно, то ориентироваться надо на 5.3, но если пишешь под более-менее контролируемую среду (заказчик возьмет хостинг какой скажешь) и/или долгоиграющий проект, то лучше на 5.4 ориентироваться. Вроде каждая мелочь в 5.4 погоды не делает, но вот в целом под 5.3 приходится себя заставлять писать чуть ли не с отвращением.
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории