Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: EventListener
Forum PHP.pl > Forum > PHP > Pro > Archiwum Pro
serafin
Jak w temacie, czy myslicie ze tworzenie specjalnej grupy obiektow do obslugi roznorodnych zdarzen: informacji debug, wyjatkow, bledow jest dobrym pomyslem?

W praktyce wygladaloby to tak:

$events = new EventListener();
$events->registerHandler('Exception', new ExceptionHandler());

// Zlapany w bloku catch() wyjatek:
$events[] = $e;

Powiadamiamy w ten sposob EventListenera o zajsciu nowego zdarzenia. Nastepnie zostaje wykonany lancuszek klas odpowiedzialnych za obsluge danego typu zdarzenia.

Co sadzicie o tym?

Pozdrawiam,
Vengeance
Czy nie chodzi ci o to samo, czym było by wykorzystanie funkcji set_exception_handler() ?
serafin
Nie, bo imho zdarzenia nie musza miec nic wspolnego z wyjatkami...
Vengeance
A może wzorzec Observer ? Sama nazwa, jak i zastosowanie spełnią chyba twoje wymagania.
NuLL
Serafin - podoba mi się to rozwiązanie smile.gif
hawk
Kilka luźnych uwag:

1) To jest chyba wykorzystanie interfejsu ArrayAccess?

2) Jak Observer, to warto (jeżeli się da) wykorzystać kod z SPL. Nic odkrywczego nie wymyślili, ale może uda się wypracować jakiś standard/

3) Bardzo by się przydał lazy loading, bo przecież ExceptionHandler przeważnie nie będzie wywoływany. Albo handle/proxy (jak w WACT lub phiend.handle), albo delegate/invoker (jak w WACT lub niepublikowanej jeszcze części phiend.handle).

4) Zmieniłbym nazewnictwo, bo EventListener sugeruje, że jest to Observer, a tymczasem jest to drugi element tego wzorca - Observable. Robiony przeze mnie pakiet phiend.logger ma klasę Logger, którza przyjmuje wiadomości/zdarzenia i kieruje do listenerów. Efekt jest chyba podobny...
NuLL
@Hawk Wątpię aby to byl ArrayAccess. Ja osobiscie tez wykorzystuje statyczny rejestr w ktorym zapisuje komunikaty -np. nie mozna sie polaczyc z baza i jechac tylko na cache o ile sie da.

Co handlerow - tez mam swoje - inicjowane wraz z jadrem systemu - choc zastanawiam sie czy jest jakas metoda ktora pozwoli na to aby ominac wywolanie handler jesli w aplikacji nie zajdzie zaden wyjatek cool.gif Logger tez jest a majac do dyspozycji Reflection Class mozna robic naprawde fajne rzeczy smile.gif
jaco
@NuLL: Twoje watpliwosci sa nieuzasadnione - chyba nie uwazasz na lekcji tongue.gif

  1. <?php
  2.  
  3. $events = new EventListener();
  4. $events->registerHandler('Exception', new ExceptionHandler());
  5. $events[] = $e;
  6.  
  7. ?>


Ciekawe jak inaczej? winksmiley.jpg
NuLL
Ale ja nie chce rejestrowac tego handlera dopoki nie jest mi potrzebny - ba, najlepiej zadnego handlera nie inicjowac. Tu robisz wywolanie jawnie inicjujac aplikacje - ja chce tego uniknac. Pomysl juz mam smile.gif
Pozatym pomysl z tablica jest poroniony. Lepiej wg mnie.
  1. <?php
  2.  
  3. Logger::Register(&#092;"/e NoSuchComponent\");
  4.  
  5. ?>

Przelaczniki ala DOS i to mi sie podoba smile.gif
serafin
Zmienilem nieco idee. Pokazuje wiec kod:

  1. <?php
  2.  
  3. // Prosty obserwator;)
  4. final class DebugPrint implements Observer {
  5.  
  6. private $subject;
  7.  
  8. public function register(Subject $s) {
  9. $this->subject = $s;
  10. $s->attach($this);
  11. }
  12.  
  13. public function unregister(Subject $s) {
  14. if($this->subject) {
  15. $this->subject->detach($this);
  16. }
  17. }
  18.  
  19. public function update(Subject $s) {
  20. $this->subject = $s;
  21.  $call = debug_backtrace();
  22.  $call = $call[2];
  23.  echo '<p style=\"font-family: Verdana; font-size: 10px; padding: 0px; margin: 0px;\"><b style=\"color: red\">Debug</b>: ' . $this->subject->getMsg() . ' <!--<a href=\"source.php?file='.$call['file'].'&line='.$call['line'].'\" target=\"_blank\">'.basename($call['file']) . ' @ line '.$call['line']. '</a>--></p>';
  24. }
  25. }
  26. // Obserwowane zdarzenie
  27. class Debug implements Subject, ISingleton {
  28. protected $msg;
  29. private $observers;
  30.  
  31. public function attach(Observer $o) {
  32. $this->observers[] = $o;
  33. }
  34.  
  35. public function detach(Observer $o) {
  36. foreach($this->observers as &$observer) {
  37. if($observer === $o) {
  38. unset($observer);
  39. }
  40. }
  41. }
  42.  
  43. public function notify() {
  44. if(count($this->observers)) {
  45. foreach($this->observers as $observer) {
  46. $observer->update($this);
  47. }
  48. }
  49. }
  50.  
  51. public static function &singleton() {
  52. static $instance;
  53.  
  54. if(!($instance instanceof Debug)) {
  55. $instance = new Debug();
  56. }
  57. return $instance;
  58. }
  59.  
  60. public function getMsg() {
  61. return $this->msg;
  62. }
  63.  
  64. public function info($msg) {
  65. $this->msg = $msg;
  66. $this->notify();
  67. }
  68. }
  69.  
  70. // ErrorHandler;)
  71. final class ErrorHandler extends Debug {
  72.  
  73. public function __construct() {
  74. }
  75.  
  76. public static function &singleton() {
  77. static $instance;
  78.  
  79. if(!($instance instanceof ErrorHandler)) {
  80. $instance = new ErrorHandler();
  81. }
  82.  
  83. return $instance;
  84. }
  85.  
  86. public function info($errno, $errstr, $errfile, $errline) {
  87. $this->msg = 'PhpError: ' . $errstr . ' [<u>' . basename($errfile) . '</u> at line <u>' . $errline . '</u>]';
  88. $this->notify();
  89. }
  90. }
  91. // Inicjacja wszystkiego
  92.  
  93. $debug = Debug::singleton();
  94. $ehandle = ErrorHandler::singleton();
  95.  
  96. set_error_handler(array($ehandle, 'info'));
  97.  
  98. /* Debug print to output */  
  99. $d = new DebugPrint();
  100. $d->register($debug);
  101. $d->register($ehandle);
  102.  
  103. /* Debug logging into file observer */
  104. $f = new FileLog();
  105. $f->register($debug);
  106.  
  107. $debug->info(&#092;"Application start\");
  108.  
  109. ?>


Jak widac skorzystalem z wzorca Obserwator. To rozwiazanie jest teraz w miarę szybkie i dosyc skuteczne. Postaram sie zaimplementowac cos jeszcze.

Pozdrawiam,

// Edit

Hawk: Co do lazy loading, masz racje obserwatorzy czy tez exceptionhandler nie zawsze beda potrzebni, w takim razie mozna nieco zmodyfikowac cala idee by zamiast obiektow przekazywac tylko nazwy odpowiednich obserwatorow.
dasko
@NuLL: Reflection API sprawdziłoby się raczej tylko w przypadku statycznych handlerów klas - tzn jesli masz handler np. XYZ::onDoSomething(). W tym wypadku moznaby albo za kazdym razem leciec reflectionem po klasachw wposzukiwaniu obsługi zdarzeń, albo (tylko raz) stworzyć mapę klas z danymi handlerami.

Ale co jeśli obiektów danego typu jest dużo? Na dodatek lokalnych w jakiejś funkcji? W tym wypadku chyba właśnie lepiej zastosować obserwatorów(chyba nawet trzeba) i po kolei ich rejestrować, tak jak zrobił serafin. Chyba nie ma innego wyjścia smile.gif

Może się mylę, poprawcie jakby co tongue.gif
Nievinny
hymm..., ja myślę, żę obserwator nie jest tu niezbędny, ale bardzo ułatwia pracę. Choć sam debug zrobił bym inaczej (wyświetlanie komunikatów na końcu) i oddzielny obserwator dla wyjątków i błędów. Ogólnie myślę, że jest wiele sposobów prowadzenia debugera i jeżeli chodzi o uniwersalność "statyczne klasy", które mają metody i właściwości statyczne są lepsze i nie trzeba znać wzorca obserwatora. Aczkolwiek eventy mi się podobają, proste i w miarę skuteczne.
Vengeance
serafin: zastanawiam się po co te register() i unregister() w Obserwatorze. Chyba przejrzyściej by wyglądało, gdybyś to w obiekcie obserowanym registrował obserwator, a nie odwrotnie.
serafin
Vengeance: rejestrujesz w Obserwatorze Podmiot ktory ma obserwowac. Wiec logiczny jest taki kierunek rejestracji winksmiley.jpg
Vengeance
A co logicznego jest w tym, że w Obserwatorze rejestruje Podmiot do obserwacji... z czego ten Obserwator tak na prawde potem rejestruje się w Podmiocie :]

Nie wydaje ci sie to drogą "na około" ?
Nievinny
Możesz wyciąć w tych metodach dodawanie do stosu podmiotów, choć ich rejestrowanie jest pomocne. A jak rejestruje się wiele podmiotów pod jednym obserwatorem to jest po prostu zbawienie.
hawk
Reflection API jest fantastyczne w przypadku wywalania na ekran informacji o błędzie. Ja np. wyciągam definicję każdej metody z backtrace, włącznie z wszystkimi przełącznikami (final, static, itd), z nazwami i typami argumentów itd. W wolnym czasie chciałem zrobić parsowanie phpdoc w celu wyciągnięcia dodatkowych informacji o argumentach i zwracanym typie (i oczywiście opisu). Jak zrobię, będę mógł zaprezentować phiend.logger biggrin.gif. Chyba że ktoś chce się tym za mnie pobawić...
serafin
hawk: przejrzalem twojego phienda2. I'm quite impressed.

Bardzo spodobal mi sie plugin loader i sposob w jaki go zaimplementowales (zwlaszcza Handles i Lazy Loading)

P.S. Nie obrazisz sie jak skorzystam z kilku twoich pomyslow ;> ?
Vengeance
Czy czasem Lazy Loader nie odnosił się bardziej to logiki biznesowej niż zarządzaniem obiektami ?!
serafin
Lazy loading odnosi sie do zarzadzania obiektami. Obiekt tworzony jest wylacznie wtedy kiedy jest potrzebny, nie wczesniej. Podobnie dziala chyba mechanizm dynamicznego wiazania w javie, obiekt tworzony jest w momencie kiedy jego instancja jest potrzebna.

pozdrawiam,
chmolu
Sorry, że odgrzewam stary temat smile.gif

Przejrzałem kod WACTa i Phienda2 i tak się zastanawiam po co są te Handles. No dobrze, obiekty są tworzone tylko kiedy są naprawdę potrzebne, ale przecież dodatkowo mamy klasę Handle, która musi być załadowana zawsze. Wg mnie to dodatkowe obciążenie, a nie żadna korzyść. Chyba, że źle rozumuję?
hawk
No ale skoro "tylko wtedy kiedy są naprawdę potrzebne", to znaczy że nie zawsze, prawda? I tutaj właśnie mamy korzyść, bo obiekt Handle/Proxy/whatever jest malutki, a obiekt docelowy zapewne większy.
emilio
Cytat(hawk @ 2005-08-28 14:40:26)
No ale skoro "tylko wtedy kiedy są naprawdę potrzebne", to znaczy że nie zawsze, prawda? I tutaj właśnie mamy korzyść, bo obiekt Handle/Proxy/whatever jest malutki, a obiekt docelowy zapewne większy.

Ja uważam że w warstwie prezentacji nie powinno się ich stosować. Utrudnia to zrozumienie kodu i co najważniejsze nie daje wymiernych korzyści. Nie ma tam takiego stopnia skomplikowania, jaki panuje w warstwie dziedziny. Czyli mamy całkowitą kontrolę nad tym co robimy, czego nie można powiedzieć o modelu dziedziny.

Konkrety, Tworzysz plik index.php a w nim inicjujesz pluginy i filtry (wszystko skoncetrowane w jednym miejscu). Wyklucza nam to problem wielokrotnego ładowania jednego, tego samego elementu (jedna z głównych przyczyn dla których stosuje się Lazy Initialization). Dwa, Stosujesz 'pustą' inicjalizację, czyli nie wykonujesz żadnych konkretnych operacji podczas tworzenia obiektu. Dopiero na samym końcu następuje właściwa faza działania programu, tzn. operacje na modelu i tym podobne rzeczy. Czyli poza znikomym spadkiem użycia pamięci (powtarzam, jest to warstwa prezentacji, tj. filtry, akcje, itp. małe klasy, które nigdy nie będą potężnymi bibliotekami) nie osiągasz nic więcej bo nie generujesz większych obciążeń podczas tworzenia obiektów (czego nie można powiedzieć o elementach warstwy dziedziny).

Posumowując, jeżeli podczas inicjalizacji nie wykonuje się 'większych' operacji (takich jak np. nawiązanie połączenia z bazą danych) to jak dla mnie, nie opłaca się stosować takich rozwiązań (pokreślam, cały czas mam na myśli warstwę prezentacji, która ma nieporównywalnie mniejszy współczynnik stopnia skomplikowania niż dziedzina czy warstwa usług).

Pozdrawiam, emilio.
chmolu
Cytat
No ale skoro "tylko wtedy kiedy są naprawdę potrzebne", to znaczy że nie zawsze, prawda? I tutaj właśnie mamy korzyść, bo obiekt Handle/Proxy/whatever jest malutki, a obiekt docelowy zapewne większy.

Hmm... ale przyznaj szczerze, ile z tych obiektów rzeczywiście nie zostanie użytych? W WACT'cie może i się to przydaje, bo tam ładujesz uchwyty do wszystkich akcji w jednym wywołaniu skryptu (przynajmniej z tego, co widziałem na przykładach). W tym wypadku wiadomo, że nie będzie potrzeby ładowania każdej klasy, bo zazwyczaj wystarczy jedna akcja.

Chociaż to też dla mnie jest mniej efektywne rozwiązanie, bo po co w każdym requescie ładować handles do wszystkich akcji?

Rozwiązanie jest ładne i ciekawe, ale ładniejsze i ciekawsze byłoby w javie tongue.gif
emilio
Cytat(chmolu @ 2005-08-28 16:33:08)
Rozwiązanie jest ładne i ciekawe, ale ładniejsze i ciekawsze byłoby w javie tongue.gif

W C++ mogę zrobić coś takiego:
Kod
int i = 23521;
long l;
l = static_cast<long>(i);
...
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.