12 Ноя 2010

ООП практикум в PHP5: Анализ ошибок, преимуществ и недостатков двух реализаций примесей в PHP

Category: PHP,СтатьиFractalizeR @ 11:40

После выхода моей статьи о примесях мы продолжали дискуссии и в комментариях к статье и в личных сообщениях. Сегодня увидел в моем блоге комментарий от читателя, который попросил объяснить, в чем я вижу преимущество своей реализации перед реализацией Леонида Шлейхера.

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

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

Мой вариант более производителен за счет использования так называемого реестра для хранения кэшированной информации о примесях. Но использованный мной вариант кэширования эффективен только при многократном обращении к методу / свойству примеси.  Если доступ одноразовый – в моем варианте он, скорее всего, будет прилично более медленным, чем у Леонида, потому что нужно сначала построить кэш. Для ликвидации этого недостатка в моем случае следует перейти от модели статического класса реестра к модели реестра-синглтона, инициализируемому через абстрактную фабрику или реализовать стандартный паттерн «реестр» уровня фреймворка. Это позволит варьировать реализацию реестра в зависимости от требований проекта и, например, заменить кэширование прямыми вызовами для ускорения одноразовых обращений к методам.

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

И я, и Леонид, похоже, допустили одну и ту же ошибку в реализации, связанную со сборкой мусора. Обратите внимание на циклические ссылки: в классе-агрегаторе хранятся ссылки на экземпляры классов примесей, и в примесях, в свою очередь, хранятся ссылки на экземпляры классов агрегаторов. В PHP версиях младше 5.3 произойдет утечка памяти, потому что сборщик мусора не умеет работать с циклическими ссылками. В 5.3 можно активировать специальный сборщик мусора для этой цели, но он замедляет работу скрипта в целом, так что это нежелательно. Что в этом плохого? Все зависит от того, для чего и как используются примеси. Если объектов-агрегаторов со своими примесями много, они большие и сложные, часто создаются / разрушаются – все будет очень плохо. Если же примеси подмешиваются к агрегаторам, существующим на всей протяженности жизни скрипта, ничего страшного не будет, так как вся занимаемая скриптом память при его завершении будет освобождена принудительно.

Как исправить эту ошибку? В обеих реализациях сделать это довольно просто, воспользовавшись не слишком часто используемой возможностью PHP – деструктором. Нужно добавить деструктор, принудительно обнуляющий ссылку на владеющий класс, в примесь и деструктор в класс-агрегатор, очищающий список примесей данного класса.

Сейчас вот оказалось, что вариант Леонида вообще не компилируется, потому что магический метод __call() в классе Caller не может быть защищенным. Исправил, заработало. В 5.3 варианте из комментария этой ошибки уже нет.

Леонид реализовал в примеси магические методы, чтобы примесь могла обращаться к членам класса-агрегатора как к своим собственным. Я этого сделать не догадался. Впрочем, я подумаю, реализовывать ли эту идею у себя. Пока что мне не очень нравится граф вызовов, который при этом получается, и некоторая расплывчатость области видимости.

Вариант Леонида статический. Код почему-то написан так, что примеси нужно создавать в конструкторе агрегатора, что, в общем-то, слегка противоречит концепции примесей.  В моем варианте примеси к классам подсоединяются динамически, причем для этого не требуется даже загрузка самих классов (ни агрегаторов, ни примесей). То есть, все примеси можно подсоединять в одном месте проекта без ущерба для производительности. В этом есть и свой недостаток: ошибку в имени класса-агрегатора или примеси сложнее отследить. Ведь классы не загружаются, пока не начнут реально использоваться.

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

За счет «подсоединения» в моем варианте примеси можно подсоединять к классам прямо в процессе исполнения основного кода. То есть, если потерять осторожность, может оказаться так, что в один экземпляр класса данная примесь подмешана, а в другой нет. Мы получили чрезмерную гибкость, которую легко устранить, если это необходимо. Кстати,  это можно сделать, исследуя кэш (реестр).

Вариант Леонида реализует не все магические методы у класса-агрегатора (основной акцент сделан на методах), но это легко исправить.

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

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

У Леонида возникает еще одна любопытная ситуация, если в примеси определить защищенный или приватный метод с каким-либо именем, а затем попытаться вызвать метод с этим именем на агрегаторе. Обратите внимание на граф вызовов. У агрегатора вызывается __call(), идет поиск метода с заданным именем среди методов примесей с помощью method_exists(). Совпадение обнаруживается, поскольку method_exists() для скрытых и защищенных членов тоже возвращает true. Производится попытка вызова этого метода уже на экземпляре примеси. Поскольку метод все же защищенный и из данной области видимости недоступен, вызывается уже магический метод примеси __call(), который, в свою очередь, вызывает псевдо-магический метод агрегатора __access_call, в который Леонид забыл добавить проверку ошибок. Поскольку проверка ошибок отсутствует, ничего не происходит и никакой ошибки мы не получаем, хотя мне лично хотелось бы. Да и сам граф сложноват получился, на мой взгляд, для реализации примесей на 80 строк… Ошибку исправить, конечно, легко.

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

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

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

Я активно пользуюсь тайпхинтингом в PHP для дополнительных проверок. У Леонида не заметил, но это пустяки.

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

Кстати, вариант автора уже работает в проекте, а мой еще нет. Лежит себе на компе без дела…

Из не относящегося к делу, могу отметить, когда на сайте читаешь код, хотелось бы видеть если уж не выделение синтаксиса цветом, то хотя бы, чтобы отступы были нормальные. Код Леонида для анализа пришлось в Zend Studio форматировать J. Приношу свои извинения, если код на сайте не предназначался для анализа посторонними. Меня оправдывает разве что то, что все же он получен из открытых источников.

На сайте Леонида в комментариях также был задан вопрос о преимуществах примесей перед обычным делегированием, но это уже тема для моей следующей статьи, которая не заставит себя долго ждать. Объем не позволяет изложить мою точку зрения на этот вопрос тут.

Огромная просьба в комментариях не сравнивать две реализации с точки зрения «плохая/хорошая». Уважайте себя. Говоря о достоинствах и недостатках, воздержитесь от демагогии. Факты, факты и еще раз факты. Троллей, обожающих кричать «УГ» и «КГ/АМ» даже не разобравшись в вопросе, просят пройти на посадку, поезд скоро отправится.

Спасибо за внимание.

14 комментариев “ООП практикум в PHP5: Анализ ошибок, преимуществ и недостатков двух реализаций примесей в PHP”

  1. OZ говорит:

    Ну а код, код-то где? Переходя по ссылке я надеялся, что «вот сейчас-то я точно его пойму» 🙂
    Надеюсь, просьбу сравнения Вы не восприняли как упрёк.
    На хабре лучше убрать в черновики, имхо — много несущественных придирок к коду, который писался 3 года назад.

    Читая Ваш текст, существенными отличиями я бы назвал: отсутствие реакции на вызов несуществующего класса у Леонида и отсутствие доступа примеси к методом аггрегатора у Вас. Остальное — мелочи. Например, возможно примешивать в процессе выполнения можно добавить одной простенькой строчкой (публичным методом с кодом $this->mixins[]=$mixin;).

    Про сравнения примеси и делегирования: вставлю текст своих размышлений из моей «записной книжки».
    Сравнил быстродействие примесей и делегирования.
    Как и ожидалось, примеси оказались 11 раз медленнее. Но… Тестировалось 1000 вызовов (полностью одинаковые алгоритмы). В реальности метод объекта, да ещё именно тот, который реализован примесью или делегированием, будет вызван раз десять максимум. На 10 вызовов примесь (в моём тесте) тратит 0.000225592 с., а делегированный метод — 0.000019407 с. Вы ощутите эти 0.0002 секунды? Это явно не узкое место системы. Пусть даже метод вызовется 100 раз — 0.002 секунды это прекрасный результат для 100 вызовов метода.
    Вобщем, обращать внимание на скорость примесей нет никакого смысла. Это оптимизация уровня print/echo.

    Пока писал классы для тестирования производительности, я понял, чем отличаются примеси от делегированного метода.

    Примесь имеет доступ к методам класса (родителя). И бывает, что использовать эту возможность очень удобно. Это означает, что конструкция «основной класс — примесь» имеет очень высокую связность. Они соединены жёстким каркасом общих методов и свойств. При изменении архитектуры одного из классов, использующих примесь, код самой примеси, с большой вероятностью, изменится тоже. То есть, примесь узко специализированна именно на определённый класс.

    Делегированный метод связан с родительским классом только точкой вызова. Делегированный метод может по ссылке получить какие-либо свойства. Значит, максимум, что нужно будет изменить — поменять код в точке вызова. Вообще, можно передать и метод по ссылке, но в PHP нет (в данный момент) указателей на функции, а передавать в массиве имя класса и метода — неправильно.

    Роли примеси и делегированного метода можно сравнить на модели фонарика.
    Делегированный метод это батарейка, примесь — выключатель.
    Код выключателя прост и писать его заново для каждого нового фонарика нет желания.
    Однако, если переоборудовать фонарик в водонепроницаемый — его конструкция заметно изменится, и выключатель — тоже. Точно так же с примесью — поскольку примесь контактирует с механизмом класса напрямую.
    Код батарейки (делегированного метода), напротив, не будет меняться даже если фонарик станет водонепроницаемым — изменится отсек для батарейки, но не батарейка.

    Примесь есть смысл использовать тогда, когда в алгоритме требуется работа с методами класса. Во всех остальных случаях разумнее (на мой взгляд), использовать делегированный метод.

  2. OZ говорит:

    Ну а код, код-то где? Переходя по ссылке я надеялся, что «вот сейчас-то я точно его пойму» 🙂
    Надеюсь, просьбу сравнения Вы не восприняли как упрёк.

    Читая Ваш текст, существенными отличиями я бы назвал: отсутствие реакции на вызов несуществующего класса у Леонида и отсутствие доступа примеси к методом аггрегатора у Вас. Остальное — мелочи. Например, возможно примешивать в процессе выполнения можно добавить одной простенькой строчкой (публичным методом с кодом $this->mixins[]=$mixin;).

    Про сравнения примеси и делегирования: вставлю текст своих размышлений из моей «записной книжки».
    Сравнил быстродействие примесей и делегирования.
    Как и ожидалось, примеси оказались 11 раз медленнее. Но… Тестировалось 1000 вызовов (полностью одинаковые алгоритмы). В реальности метод объекта, да ещё именно тот, который реализован примесью или делегированием, будет вызван раз десять максимум. На 10 вызовов примесь (в моём тесте) тратит 0.000225592 с., а делегированный метод — 0.000019407 с. Вы ощутите эти 0.0002 секунды? Это явно не узкое место системы. Пусть даже метод вызовется 100 раз — 0.002 секунды это прекрасный результат для 100 вызовов метода.
    Вобщем, обращать внимание на скорость примесей нет никакого смысла. Это оптимизация уровня print/echo.

    Пока писал классы для тестирования производительности, я понял, чем отличаются примеси от делегированного метода.

    Примесь имеет доступ к методам класса (родителя). И бывает, что использовать эту возможность очень удобно. Это означает, что конструкция «основной класс — примесь» имеет очень высокую связность. Они соединены жёстким каркасом общих методов и свойств. При изменении архитектуры одного из классов, использующих примесь, код самой примеси, с большой вероятностью, изменится тоже. То есть, примесь узко специализированна именно на определённый класс.

    Делегированный метод связан с родительским классом только точкой вызова. Делегированный метод может по ссылке получить какие-либо свойства. Значит, максимум, что нужно будет изменить — поменять код в точке вызова. Вообще, можно передать и метод по ссылке, но в PHP нет (в данный момент) указателей на функции, а передавать в массиве имя класса и метода — неправильно.

    Роли примеси и делегированного метода можно сравнить на модели фонарика.
    Делегированный метод это батарейка, примесь — выключатель.
    Код выключателя прост и писать его заново для каждого нового фонарика нет желания.
    Однако, если переоборудовать фонарик в водонепроницаемый — его конструкция заметно изменится, и выключатель — тоже. Точно так же с примесью — поскольку примесь контактирует с механизмом класса напрямую.
    Код батарейки (делегированного метода), напротив, не будет меняться даже если фонарик станет водонепроницаемым — изменится отсек для батарейки, но не батарейка.

    Примесь есть смысл использовать тогда, когда в алгоритме требуется работа с методами класса. Во всех остальных случаях разумнее (на мой взгляд), использовать делегированный метод.

  3. admin говорит:

    Не совсем понимаю, какой код вы от меня ожидали 🙂 Я ведь уже все свое выложил, а код Леонида доступен на его сайте. Вы просили оценить различия, а не «только существенные различия», вот я и оценил. То, что вы называете «существенными» таковыми в другом проекте может не оказаться.

    Что касается скорости примесей — все опять же зависит от ситуации. Без кеширования при большом количестве примесей все может быть еще медленее, чем 11 раз. А если проект завязан на постоянные вызовы примесей… В общем, преждевременная оптимизация, как вы правильно заметили, корень всех зол, но и совсем не обращать внимания на это не стоит. Все нужно решать, когда есть реальная задача.

    Про делегированные методы чуть позже 🙂 Мне надо подумать 🙂

    В общем, еще раз спасибо за проявленное внимание.

  4. OZ говорит:

    Не совсем понимаю, какой код вы от меня ожидали

    Код без кэширования. Чтоб ясно увидеть, в чём суть модели, основной алгоритм. Код ведь, как я понимаю, был написан больше для теоретического аспекта. Если сложно выделить код без кэширования — не стоит заморачиваться.

  5. admin говорит:

    А-а, теперь понял 🙂
    Код без кеширования будет чуть позже. Я попробую, как и обещал, перейти от статического реестра и тогда все будет. Но будет похоже на реализацию Леонида в достаточно большой степени. Если я еще что-нибудь интересного не придумаю.

  6. admin говорит:

    но в PHP нет (в данный момент) указателей на функции, а передавать в массиве имя класса и метода – неправильно.

    Есть анонимные функции в 5.3, кстати.

  7. OZ говорит:

    Передавать метод в виде анонимной

  8. OZ говорит:

    …функции — как-то совсем уж извращённо.

  9. admin говорит:

    Не отрицаю 🙂

  10. joker2k1 говорит:

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

    __minh as $minhObj)

    if ($minhObj->__minh_isMethodExists($method))

    return $minhObj->__minh_method($method, $params);

    throw new Exception(«Unknown method $method\n»);

    }

    function __get($name) {

    foreach($this->__minh as $minhObj)

    if ($minhObj->__minh_isPropertyExists($name))

    return $minhObj->__minh_get($name);

    throw new Exception(«Unknown property $name\n»);

    }

    function __set($name, $value) {

    foreach($this->__minh as $minhObj)

    if ($minhObj->__minh_isPropertyExists($name)) {

    $minhObj->__minh_set($name, $value);

    return;

    }

    throw new Exception(«Unknown property $name\n»);

    }

    // multi inheritance

    function __minh_isMethodExists($method) {

    return method_exists($this, $method);

    }

    function __minh_isPropertyExists($name) {

    return property_exists($this, $name);

    }

    function __minh_method($method, $values) {

    if (method_exists($this, $method))
    return call_user_func_array(array($this, $method), $values);

    }

    function __minh_get($name) {

    if (property_exists($this, $name))
    return $this->$name;

    }

    function __minh_set($name, $value) {

    if (property_exists($this, $name))
    $this->$name = $value;

    }

    //

    function __minh_add($obj) {

    $this->__minh[] = $obj;

    }

    }

    //

    class A extends base9 {

    var $z;

    function Say($text) {

    echo $text;

    }

    }

    class B extends base9 {

    var $p;

    function SayTwice($text) {

    echo $text.$text;

    }

    }

    class C extends base9 {

    var $a = 0;

    function SetTimes($a) {

    $this->a = $a;

    }

    function SayN($text) {

    for($i=0;$ia;$i++)
    echo $text;

    }

    }

    $test = new A();
    $test->Say(«test»);
    $test->__minh_add( new B() );
    $test->SayTwice(«gogo»);

    $test->z = 10;
    $test->p = 5;

    echo $test->p;

    $test->__minh_add( new C() );
    $test->a = 3;
    $test->SayN(«t»);

    ?>

    вот один класс, может быть и примесью, и примешиваться к чему угодно 🙂 по сути множественное наследование ран-тайм.

    там не стал вдаваться в детали, главное идея.
    если будет интересно, пишите jokervaio@gmail.com
    может инвайтом поделитесь, мне хабр понравился 🙂

  11. joker2k1 говорит:

    ой. чета код не весь. еще раз.

    <?php
      class base9 {
        var $__minh = array();
        // 
        function __call($method, $params) {
          foreach($this->__minh as $minhObj) 
            if ($minhObj->__minh_isMethodExists($method))
              return $minhObj->__minh_method($method, $params);
          throw new Exception("Unknown method $method\n");
        }
        function __get($name) {
          foreach($this->__minh as $minhObj) 
            if ($minhObj->__minh_isPropertyExists($name))
              return $minhObj->__minh_get($name);
     
          throw new Exception("Unknown property $name\n");
        }
        function __set($name, $value) {
          foreach($this->__minh as $minhObj) 
            if ($minhObj->__minh_isPropertyExists($name)) {
              $minhObj->__minh_set($name, $value);
     
              return;
     
            }
     
          throw new Exception("Unknown property $name\n");
        }
        // multi inheritance 
        function __minh_isMethodExists($method) {
          return method_exists($this, $method);
        }
        function __minh_isPropertyExists($name) {
          return property_exists($this, $name);
        }
        function __minh_method($method, $values) {
          if (method_exists($this, $method))
            return call_user_func_array(array($this, $method), $values);
        }
        function __minh_get($name) {
          if (property_exists($this, $name))
            return $this->$name;
        }
        function __minh_set($name, $value) {
          if (property_exists($this, $name))
            $this->$name = $value;
     
        }
        //
        function __minh_add($obj) {
          $this->__minh[] = $obj;
        }
      }
      //
      class A extends base9 {
        var $z;
        function Say($text) {
          echo $text;
        }
      }
      class B extends base9 {
        var $p;
        function SayTwice($text) {
     
          echo $text.$text; 
        }
      }
      class C extends base9 {
        var $a = 0;
        function SetTimes($a) {
          $this->a = $a;
        }
        function SayN($text) {
          for($i=0;$ia;$i++)
            echo $text;
        }
      }
      $test = new A();
      $test->Say("test");
      $test->__minh_add( new B() );
      $test->SayTwice("gogo");
      $test->z = 10;
      $test->p = 5;
      echo $test->p;
      $test->__minh_add( new C() );
      $test->a = 3;
      $test->SayN("t");

    может это сподвигнет вас на еще одну статью, с разбором полетов 🙂

  12. FractalizeR говорит:

    Дело в том, что помещение всего функционала в один класс — это не всегда хорошо. Вам сейчас кажется, что это просто и это, может быть, действительно так. Но попробуйте сейчас добавить в вашу модель кеширование доступа как у меня, возможность доступа примеси к защищенным полям агрегатора как у Леонида или у меня? Код сильно вырастет и его станет труднее поддерживать. А потом попробуйте добавить возможность выбора произвольного механизма кеширования в проекте. Что станет тогда? А механизм проверки типов на этапе подмешивания с регистрацией?

    Ваш код не слишком понятен, а base9 вообще странное имя для примера 🙂 Что оно значает? Почему 9?

    Проследите граф вызовов при вызове метода. __call -> __minh_isMethodExists -> method_exists -> __minh_method -> method_exists. Зачем двойная проверка?

    Ваша модель требует загрузки всех классов для подмешивания и требует подмешивания на уровне объектов. Не вызвал __minh_add = ничего не подмешалось. Для добавления в класс примеси придется проследить все случаи инстанциации и добавить вызов __minh_add везде. В моем случае все решается одной строкой на проект. Даже у Леонида достаточно один вызов в конструкторе добавить. Это неудобно. Еще неудобнее простого делегирования, как мне кажется.

  13. joker2k1 говорит:

    ok 🙂 я хотел показать вам идею, вы ее не увидели 🙂

    1. кеширование здесь не нужно, оно у вас есть от того, что ваша модель не оптимальна.
    2. защищенные поля не нужны, ведь сама идея множественного наследования чужда для пхп, и ради нее я готов отказаться от глупых надписей protrcted и private. вы для кого их пишете? мне главное чтобы работало, быстро, хорошо, умно и оптимально. надписи private и protected этому никак не способствуют, толи дело множественное наследование.
    3. название класса взято с потолка — чем оно хуже ваших названий? :))) это всего лишь набор букв — зрите в корень.
    4. финальная реализация, уже вылизанная, но без отлова ошибок, если надо могу добавить, это все детали — главное суть.

    class base9 {

    var $__minh = array();

    //

    function __call($method, $params) {

    foreach($this->__minh as $minhObj)

    if (method_exists($minhObj,$method))

    return call_user_func_array(array($minhObj, $method), $params);

    throw new Exception(«Unknown method $method\n»);

    }

    function __get($name) {

    foreach($this->__minh as $minhObj)

    if (property_exists($minhObj, $name))

    //return $minhObj->__minh_get($name);
    return $minhObj->$name;

    throw new Exception(«Unknown property $name\n»);

    }

    function __set($name, $value) {

    foreach($this->__minh as $minhObj)

    if (property_exists($minhObj, $name)) {

    $minhObj->$name = $value;

    return;

    }

    throw new Exception(«Unknown property $name\n»);

    }

    function __minh_set($name, $value) {

    $this->$name = $value;

    }

    //

    function __minh_add($obj) {

    $this->__minh[] = $obj;

    }

    }

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

    с уважением, евгений 🙂

  14. FractalizeR говорит:

    1. кеширование здесь не нужно, оно у вас есть от того, что ваша модель не оптимальна.

    Поясните. На мой взгляд оптимальность модели тут ни при чем. Попробуйте добавить штук 10 примесей с 10 методами каждая в вашу реализацию и посмотрите на скорость. Кэширование понижает временные затраты на вызов методов. Нужно оно или нет говорить можно только применительно к какой-то конкретной ситуации, когда мы поймем, «тормозит» данная конкретная вещь или нет. Я напоминаю, что мы рассматриваем только концепт. Как вы можете говорить, что что-то не нужно, если у вас еще даже ситуации нет, которую можно было бы рассматривать?

    защищенные поля не нужны, ведь сама идея множественного наследования чужда для пхп, и ради нее я готов отказаться от глупых надписей protrcted и private. вы для кого их пишете? мне главное чтобы работало, быстро, хорошо, умно и оптимально. надписи private и protected этому никак не способствуют, толи дело множественное наследование.

    Я в отличие от вас от protected и private отказаться не готов. Но это просто наши личные предпочтения. Опять же напоминаю, что мы рассматриваем концепт, а не его применение в конкретном проекте.

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

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

    Я просто сейчас объясняю, чем наши реализации отличаются. Кстати, в вашей концепции есть одна хорошая идея, которой, похоже, вы сами не заметили. Примесь к примеси 🙂 Я подумаю об этом. Спасибо за пример.

    P.S. Возьмите мою модель и уберите из нее кеширование. Как вам? Разве намного сложнее, чем ваша?

Ответить

Для отправки комментария вам нужно зарегистрироваться. Войти.