Pull to refresh

Comments 28

У вас Eventable получился Пабликом Морозовым, скажем protected метод есть, напрямую его вызвать не дало бы, но вот так сработает. А все потому что вот эта ветка делает бяку:
if( method_exists($this, $name) )
call_user_func_array(array(&$this, $name), $args);

class Protect extends Eventable {
protected function foo() {
echo «Foo»;
}
}

$protect = new Protect();
$protect->foo();
Важное замечание. В принципе, легко лечится использованием reflection.
Да можно просто убрать эту ветку, публичный метод и без нее вызовет, а закрытые вызывать не надо.
Действительно, не заметил, спасибо.
Можно и так писать, но это совсем не OOP-style. Особенно публичное свойство onA, которое можно по-всякому менять снаружи, и которое является одновременно объектом, массивом и методом.
Чем не угодил паттерн в классической реализации (типа java.util.Observable в Java) с методами addObserver, deleteObserver и notifyObservers?
ниже я даже привел ссылку на SPL реализацию именно этого подхода…
является одновременно объектом, массивом и методом

В этом и заключается удобство данного подхода.
Коллбэками можно удобно управлять, используя логику работы с массивами (при желании, можно даже запускать определённый коллбэк, ну это если совсем приспичит), а запуск цепочки происходит так как, если бы это был простой метод. В коде это выглядит очень опрятно, без нагромождения монструозных конструкций.
Люблю минимализм :)
А вот я у себя в вики тоже подборку паттернов пополняю время от времени, если кому-то интересен не только Observer, то посмотреть текущий список можно здесь: wiki.summercode.com/projects/wiki/wiki/Design_Patterns
Чтобы избавится от костыля нужно использовать статику, но там появляется свой совсем маленький костыленок :) pastie.org/1232329
А разве в таком случае не получится, что для всех объектов класса Test будут одинаковые коллбэки?
получится, да, всем известно что решение одного бага часто приводит к появлению другого бага :) Так что такой подход без костылей судя по всему невозможен :)
решение одного бага привело к неправильной реализации
1) передача параметров по ссылке deprecated.
2) ArrayObject вы нашли, а 2 интерфейса рядом нет? ru.php.net/manual/en/spl.misc.php
3) __call не вызывается, если доступный метод существует [проверка if( method_exists($this, $name)) лишняя] и, вообще, что мешает использовать все тот же call_user_func?
Добавлю ещё, что предоставленный автором метод усложняет понимание того, что должно было и происходит в коде. В сложных системах с множеством вызовов наступит полный капец.

Лично я реализовал-бы систему с помощью простого и ясного метода $this->onA->message('This is A', $txt).
1) Да, спасибо, я заметил это немного позднее, не успел поправить текст статьи.
2) Буду посмотреть
3) Это отличная новость! Я сам не сообразил проверить код на возможность подобного, но раз это есть, значит можно избежать вызова private!
__call не вызывается, если метод существует и он публичный: __call() is triggered when invoking inaccessible methods in an object context.
вот именно, что не паблик, а доступный из текущего контекста, поэтому я и написал «доступный метод»
Прочитав недавно нововведения PHP 5.3, я обратил внимание на несколько интересных особенностей, скомпоновав которые можно получить реализацию шаблона проектирования Observer, гораздо красивее, чем имеющиеся в pear и symfony

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

Просто вызовите __invoke() напрямую. Так сработает как с 'invokable' объектами, так и с анонимной функцией (которая на самом деле тоже объект).

<?php
    class Event
    {
        public function __invoke()
        {
            var_dump(__METHOD__);
        }
    }

    class a
    {
        public $onBB = null;
        public function __construct()
        {
            $this->onBB = new Event;
        }
        public function q()
        {
            $this->onBB->__invoke();
        }
    }

    $q = new a;
    $q->q(); // string(15) "Event::__invoke"
    $q->onBB = function() {
        var_dump(__METHOD__);
    };

    $q->q(); // string(9) "{closure}"
Под костылём я имел ввиду запись вида $this->{$name}.
А вызов __invoke(); напрямую — это то, от чего я пытался уйти. С тем же успехом можно было бы писать fireEvent и эта реализация ничем не отличалась бы от сотен предложенных ранее. К тому же, разве вызов __invoke() напрямую позволит передать аргументы в функцию?
в пхп позволит
<?php
function foo($a, $b)
{
    $v = func_get_args();
    for ($i=0;$i<func_num_args();$i++) echo $i,' ',$v[$i].PHP_EOL;
}
foo('sdf',4,'46',array(1,2,3));
Да, я знаю про func_get_args(). У меня просто были сомнения, что магическая функция поведёт себя точно также как и любая другая в данном случае.
С тем же успехом можно было бы писать fireEvent и эта реализация ничем не отличалась бы от сотен предложенных ранее.

тем не менее это проще костыля с еще одним магическим методом. Альтернатива — копировать во временную переменную:
<?
//....
public function something() {
   $e = $this->onBB:
   $e($arg1, $arg2);
}
//...


К сожалению, оператор () в PHP на выражениях не вызовешь.
Не очень мне нравится абстракционизм и излишняя минимализация. Я за четкие и понятные интерфейсы в ООП!
В контексте шаблона Observer я голосую за SPL
а не будет ли еррора при func_get_args() в качестве аргумента для другой функции? :)
Sign up to leave a comment.

Articles