Pierwszy projekt WWW zrealizowany :-)

Po długiej przerwie Śpiechu znowu nadaje... Mam nadzieję, że tym razem częściej. Zwłoka wynikła z tego, że ponad miesiąc po godzinach pracy w bibliotece pracowałem nad projektem dla WRiTV Uniwersytetu Śląskiego.

Efekt można zobaczyć tutaj.

Można powiedzieć, że do tego typu roboty przygotowywałem się 2 lata. Co składa się na całokształt strony?

  • 3300 linii kodu PHP,
  • 600 linii kodu HTML,
  • 380 linii kodu CSS,
  • 80 linii kodu JavaScript

Zastosowane narzędzia "dobajerowujące":

  • jQuery,
  • jQuery Uploadify,
  • jQuery Lightbox,
  • Tiny MCE

Strona została napisana "od zera", tzn. wszystko co składa się na stronę to mój autorski projekt. Mamy tutaj np.:

  • autorska galeria (tworzy miniaturki zadanego rozmiaru, skaluje zdjęcia, dodaje do bazy danych),
  • autorska captcha,
  • ajaksowy uploader plików z normalizacją nazw,
  • procesor tekstu dodający twarde spacje za spójnikami,
  • system newsów dynamicznych (tzn. pobieranych z bazy),
  • system stron statycznych (tzn. z pliku),
  • obsługa "ładnych linków" za pomocą mod_rewrite,
  • możliwość edycji tego wszystkiego w miarę prosty sposób

Jednocześnie odcinam się od strony graficznej. Wykonała ją moja żona. Bez bicia przyznaję się, że nie znam się na tym. Ja tylko dostałem obrazek w pdf i miałem przełożyć go na XHTML/CSS. Pod różnymi przeglądarkami trochę kuleje wygląd, ale ciągle nad tym pracuję :-)

Ogólnie mówiąc, uniwersytet dostał "dużo za niedużo". Nie powiem ile ich to kosztowało, bo zostanę posądzony za "psucie rynku" (ta uwaga w kontekście ostatniego wykopu).

Update 25.01.2010: Uznałem, że RSS warty był zaimplementowania. Tym sposobem dochodzi do witryny jeszcze autorski generator RSS ;-)


Informatyczne lektury do poduchy

Od ponad roku wszelkimi sposobami (Allegro, biblioteka) staram się zdobywać książki z serii Head First. Muszę przyznać, że to chyba najlepsza seria książek informatycznych jaka powstała. W żartobliwy sposób, prawie od zera jesteśmy stopniowo wprowadzani w dane zagadnienie. Czytając, podążamy prawdopodobną (złą) ścieżką wtajemniczenia, a następnie otrzymujemy wytłumaczenie dlaczego jest zła :-) Do znudzenia czasem wałkuje się ten sam temat z różnych stron. Malkontenci marudzą na zbytnie marnotrawstwo miejsca na marginalia i duże obrazki, ale moim zdaniem to właśnie to odróżnia serię od konkurencji.

Ja swoją przygodę z Head First za namową wykładowcy zacząłem od "Object-Oriented Analysis&Design". Potem było "Design Patterns" i nagle okazało się, że programować w PHP można w zupełnie inny sposób niż "ciurkiem". Patrząc z perspektywy czasu, należało PHP również rozpocząć od HF. SQL i Java tak samo. Błędem w moim przypadku było zażyczenie sobie na urodziny "Thinking in Java" do nauki Javy od zera. Skutecznie na jakiś czas dałem sobie spokój z Javą (ale całe szczęście zacząłem od nowa po przeczyteniu "Head First Java"). Nie mam oczywiście nic do "TiJ", ale poziom trudności dla nowicjusza jest po prostu za duży.

Ostatnio w moje łapki trafiła pozycja wydawnictwa Packt Publishing "Programowanie obiektowe w PHP 5" autora obco brzmiącego nazwiska Hasina Haydera. Helion wydaje książki tego wydawnictwa w sporo niższych cenach niż zwykle (pewnie biorą mniejszą marżę od O'Reilly i oczywiście średnia liczba stron jest mniejsza). Po przeczytaniu również muszę stwierdzić, że książka "daje radę". Autor wprowadza nas w rzeczy, które najprawdopodobniej nam się mogą przydać, w razie czego odsyłając do dokumentacji ;-) Łatwo serię rozpoznać po ujednoliconej szacie graficznej.

Eh, jakie życie byłoby piękne gdyby Helion przekazywał egzemplarz obowiązkowy bibliotekom tak jak mówi ustawa... (myślę to w czasie kiedy myślę o "Myśleniu obiektowym w programowaniu" Matta Weisfelda, które stanie się moim następnym celem).


O pehapowcu, który chciał zostać javowcem

Znowu zleciał prawie miesiąc od ostatniego wpisu... Nie ozanacza to oczywiście, że przez ten czas nic się nie działo. Wręcz przeciwnie, a to za sprawą próby "wgryzienia się" w Javę.

Na różnych mądrych forach wyczytałem, że PHP to właściwie zabawka, a prawdziwe programowanie zaczyna się dopiero od Javy. Szkoda tylko, że niektórzy nie zauważają, że "zabawki" typu Allegro napisane są w PHP. Dodatkowo zachęcony hasłem "write once, use everywhere" zainstalowałem sobie NetBeans na Ubuntu i "ruszyłem z tym koksem".

Co nowego zauważyłem w Javie z punktu widzenia znajomości wyłącznie PHP (nie liczę Logo, Basic a potem Turbo Pascal za dzieciaka):

  • Nie ma w Javie zmiennych bez wcześniejszego zadeklarowania ich typu. Koniec ze zmienną będącą raz integerem, a innym razem stringiem.
  • Koniec z funkcjami zwracającymi wartości ładnie w PHPDoc nazwane mixed, czyli jak wszystko fajnie poszło, to return true, jak niefajnie to false, a jak trochę fajnie to stringa.
  • Przeciążanie metod to jest to czego mi brakuje w PHP. Wiele zadań programistycznych dałoby się łatwo i szybko rozwiązać gdyby można było przeciążać metody (np. różne warianty konstruktora).
  • Tablice mają z góry ustaloną liczbę elementów. Z drugiej strony mamy rozbudowane implementacje kolekcji typu List, Set i Map (w których słabo się jeszcze poruszam na dzień dzisiejszy).
  • Cała nowa grupa zagadnień (i angielskiego słownictwa) w ogóle nie występujących w PHP, czyli współbieżność (problem dostępu wielu wątków do tych samych danych, zakleszczenia, wzorzec producent-konsument itd.)
  • Ciekawe rozwiązanie "niuansików OSowych", np. w windowsach ścieżki dostępu tworzy się znakiem \ , a w uniksach / . Wpisując File.pathSeparatorChar Java sama dobierze sobie odpowiedni znak.
  • Budowa interfejsu graficznego i jego oprogramowywanie (nieźle rozwiązane w NetBeans).
  • Wszystko jest obiektem i dziedziczy po Object!
  • Nie da się niechlujnie pisać kodu :-) Towarzysz kompilator (to również nowość) od razu zgłosi, że mu się coś nie podoba.

Z punktu widzenia czytelnika tego bloga zapewne wygląda to tak jakbym zabierał się za wszystko tylko po łebkach. Może i trochę tak jest, że szybko się czymś nudzę, ale w tym przypadku chodzi o czystą ciekawość czy rzeczywiście "poważna Java" jest aż tak trudna do zrozumienia. Po miesiącu nauki stwierdzam, że nie jest to takie trudne. Oczywiście mam tutaj na myśli same podstawy języka (bez tych tysięcy bibliotek). Samo czytanie dokumentacji to już wyzwanie :-)


Atak klonów w PHP

Dzisiaj wieczorkiem rozpracowywałem sobie klonowanie obiektów w PHP. Dokładnie zainteresowała mnie metoda __clone(). W manualu PHP za dużo nie napisali, więc naskrobałem sobie krótki kod do przeprowadzenia testów. Chodziło mi o to czy podczas wykonywania klonowania obiektu uruchamiany jest konstruktor oraz czy można jakoś numerować klony (a co za tym idzie - "na gorąco" przypisywać polom sklonowanego obiektu jakieś wartości). Poniżej podaję kod z komentarzem.

<?php
class Klon {

   // Ogolna liczba klonow
   private static $liczbaKlonow = 0;
   // Nr konkretnego klona
   private $klonNr;

   public function __construct() {
      // Na etapie konstruktora przypisujemy polu klonNr wartość 0,
      // jako iż nie jest żadnym klonem
      $this->klonNr = 0;
      // Dodatkowo wywalamy na ekran komunikat upewniający nas, że uruchomiono konstruktor
      echo 'Uruchomiono konstruktor i stworzono obiekt Klon!<br />';
   }

   public function __clone() {
      // Zwiększamy licznik liczby wszystkich klonów
      self::$liczbaKlonow += 1;
      // Przypisujemy aktualną wartość licznika nowo utworzonemu klonowi
      $this->klonNr = self::$liczbaKlonow;
   }

   // Proste sprawdzenie czy obiekt jest klonem, a jeżeli tak to którym
   public function getKlonNr() {
      if ($this->klonNr == 0) return 'nie jest klonem';
      return 'jest ' . $this->klonNr . ' klonem';
   }

   // Zwrócenie liczby wszystkich klonów obiektu
   public function getLiczbaKlonow() {
      return self::$liczbaKlonow;
   }
}

$oryg = new Klon();
// Klonujemy "oryginalny" obiekt
$klon1 = clone $oryg;
$klon2 = clone $oryg;
// Klonujemy klon
$klon3 = clone $klon2;

// Wypisujemy "kto jest kim"
echo 'Zmienna $oryg ' . $oryg->getKlonNr() . '<br />';
echo 'Zmienna $klon1 ' . $klon1->getKlonNr() . '<br />';
echo 'Zmienna $klon2 ' . $klon2->getKlonNr() . '<br />';
echo 'Zmienna $klon3 ' . $klon3->getKlonNr() . '<br />';

echo 'Wszystkich klonow: ' . $oryg->getLiczbaKlonow() . '<br />';
?>

Uruchomienie kodu wyświetli:
Uruchomiono konstruktor i stworzono obiekt Klon!
Zmienna $oryg nie jest klonem
Zmienna $klon1 jest 1 klonem
Zmienna $klon2 jest 2 klonem
Zmienna $klon3 jest 3 klonem
Wszystkich klonow: 3

Wnioski:

  • podczas klonowania nie jest uruchamiany konstruktor,
  • metoda __clone() wywoływana jest po sklonowaniu obiektu, a zmieniając coś poprzez $this->cośTam operujemy już na klonie!
  • prywatne zmienne statyczne to fajna zabawa :-)

O konsumpcji śledzia i szerzącej się głupocie

Na początku września nasz "ulubiony" serwis społecznościowy, czyli Nasza-Klasa.pl dodała nowy bajer: Śledzik. W skrócie jest to prostsza kopia takich serwisów microbloggingowych jak Twitter czy rodzimy Blip. Wg mnie może być bardzo przydatna, ale...

Od momentu wprowadzenia nowej funkcjonalności dzieje się coś dziwnego. Wszyscy użytkownicy jednoczą się w celu "zatopienia śledzia". To jednoczenie polega na całkowicie bezmyślnym wklejaniu różnego rodzaju kodów wyłączających. Hiciorem wg mnie jest 5P13RD4L4J-5L3D21U wklejane przez wykształconych znajomych. Jest to tak zabawne (i żenujące), że stało się pożywką dla JoeMonstera. Okazuje się, że nie tylko moich znajomych opanowała jakaś dziwna fala "antyśledzikowa", ale jest to działanie ogólnopolskie. Powstał nawet "blog antyśledziowy".

Skoro nie podoba się Śledzik, to po prostu nie używajcie. Spamując głupimi linkami uniemożliwiacie korzystanie z nowej funkcjonalności zainteresowanym. Praktycznie od początku istnieją co najmniej dwa sposoby wyłączenia "śledzia":

  • "legalna" - poprzez kliknięcie przycisku "nie śledź" przy każdym znajomym
  • "nielegalna" - poprzez skorzystanie z instrukcji zawartych np. tutaj lub jeżeli macie Firefoksa i Adblock Plusa do reguł blokowania dodajecie nasza-klasa.pl#DIV(id=sledzik_box)

W ten co teraz sposób kompromitujecie się coraz bardziej. Wklejajcie dalej różne głupoty, a ich twórcy będą się nieźle bawić.

Źródło: demotywatory.pl

Aktualizacja z 30.09.2009, pojawił się nowy kandydat na hicior śledzikowy:

J3273M-D38113M <-- Wklej cala ta wiadomosc, razem z tym kodem na sledzika, a on zniknie NATYCHMIAST! Potwierdzone przez administratora portalu Nasza-Klasa.pl!


Nowa Opera 10 i blokowanie reklam

Dzisiaj ukazała się najnowsza stabilna wersja Opery, opatrzona numerkiem 10. Przyznam się, że ostatnio Operę używałem w czasie, gdy jeszcze miała u góry nieznośny banner reklamowy. Jak tylko pojawił się Firefox, ma się rozumieć bez żadnych reklam, po jakimś czasie w Operze zlikwidowano reklamy. Ja tymczasem już na stałe zadomowiłem "ognistego liska" u siebie na kompie i tak już zostało do dzisiaj. No ale nie o tym miała być dzisiaj mowa.

Na stronie www.opera.com mamy ładną paczkę deb przygotowaną specjalnie pod Ubuntu. Niestety wszystkie przeglądane strony wyglądają hmm... zbyt "ruchomo i kolorowo" (a to za sprawą wszechobecnych reklam, od których odwykłem). Spróbowałem tego ustrojstwa się pozbyć. Poniżej recepta:

Wyłączamy Operę.

W naszym katalogu domowym znajdujemy ukryty katalog .opera i tworzymy tam pusty plik o nazwie urlfilter.ini

Do pustego pliku wklejamy ten tekst:

Opera Preferences version 2.1
; Do not edit this file while Opera is running
; This file is stored in UTF-8 encoding

[prefs]
prioritize excludelist=1

[include]
*

[exclude]

Następnie ze strony http://niecko.pl/adblock/adblock.txt wklejamy całą zawartość począwszy od *ad2.* aż do whitelist. Dodatkowo możemy wkleić adresy stąd. Ostrzegają jednak, że im więcej wpisów tym wolniej uruchomi się Opera. Zapisujemy plik.

Odpalamy Operę i w razie napotkania reklam używamy prawego przycisku na wolnym polu i opcji zablokuj zawartość... w celu uzupełnienia filtrów (dodadzą się na koniec urlfilter.ini).


Kadu i nowy system powiadomień w Ubuntu 9.04 - Jaunty Jackalope

Mark Shuttleworth z Canonical jakiś czas temu pisał na swoim blogu na temat zamiaru wprowadzenia w Ubuntu 9.04 ujednoliconych "dymków" aplikacji. Nowy system powiadomień nazywa się Notify-OSD (więcej można poczytać np. tu). Zasadniczą rewolucją jest to, że na powiadomieniach nie może być wykonywana żadna akcja typu rozpoczęcie rozmowy, uaktywnienie programu czy coś takiego. Można jedynie sobie popatrzeć. Toczyła (i zresztą toczy się nadal) dyskusja czy to dobry krok. Na pewno warte pochwalenia jest to, że następuje próba unifikacji powiadomień. Obecnie mamy tak, że każda aplikacja powiadamia po swojemu, w dowolnym miejscu ekranu i z dowolną szatą graficzną.

Jedną z takich "opornych aplikacji" jest używane chyba przez każdego Kadu. Ktoś na forum nawet wrzucił im temat, ale deweloperzy nie są zbytnio zainteresowani. Tłumaczyli to tym, że zapewne niebawem każda dystrybucja linuksowa będzie miała swój system powiadomień i trzeba będzie dostosowywać Kadu do wszystkiego. Postanowiłem trochę "powęszyć w temacie", pokombinować i... udało się :-)

Chcecie mieć tak u siebie?

Jeżeli tak, to zarezerwujcie sobie 10 min czasu i czytajcie poniżej receptę.

  1. W terminalu wpisujemy sudo apt-get install libnotify-bin
  2. Uaktywniamy dwukrotnym kliknięciem moduł exec_notify w Zarządcy modułów w Kadu.
  3. W Konfiguracja Kadu -> Powiadomienia będziemy zaznaczać Wykonaj polecenie i odhaczać Dymki.
  4. W zakładce zdarzenie:
    • Nowa rozmowa wpisujemy notify-send -u low -c im -i /usr/share/kadu/themes/icons/default/kadu/big_message.png "%n" "rozpoczął nową rozmowę"
    • Nowa wiadomość notify-send -u low -c im -i /usr/share/kadu/themes/icons/default/kadu/big_message.png "%n" "przesyła nową wiadomość"
    • Błąd połączenia notify-send -u low -c im -i /usr/share/kadu/themes/icons/default/kadu/dialog-warning.png Kadu "błąd połączenia"
    • Dostępny notify-send -u low -c im -i /usr/share/kadu/themes/icons/default/kadu/big_online.png "%n" "zmienił status na dostępny"
    • Zajęty notify-send -u low -c im -i /usr/share/kadu/themes/icons/default/kadu/big_busy.png "%n" "zmienił status na zajęty"
    • Ukryty notify-send -u low -c im -i /usr/share/kadu/themes/icons/default/kadu/big_invisible.png "%n" "zmienił status na ukryty"
    • Niedostępny notify-send -u low -c im -i /usr/share/kadu/themes/icons/default/kadu/big_offline.png "%n" "zmienił status na niedostępny"
    • Przychodzący transfer notify-send -u low -c im -i /usr/share/kadu/themes/icons/default/kadu/kadu-transfer-receive.png "%n" "chce przesłać plik"
    • Transfer zakończony notify-send -u low -c im -i /usr/share/kadu/themes/icons/default/kadu/kadu-transfer-receive.png "%n" "zakończył przesyłać plik"
  5. Nie zapominamy zatwierdzić wszystkiego.

Garść uwag:

  • Ikony wykorzystujemy oryginalne z Kadu, u wszystkich powinny być w tym samym miejscu.
  • Od razu ostrzegam, że dwóch ostatnich poleceń nie testowałem.
  • Polecenia wpisywane są trochę na wyrost z uwzglednieniem priorytetu (low) i kategorii powiadomienia (im) - więcej tutaj. Będzie również działało bez tych parametrów.
  • Jeżeli ktoś ma obiekcje, że powiadomienie wyświetla się za wolno lub za szybko - może eksperymentować z parametrem -t liczba_milisekund (zajrzeć do man notify-send).

To tyle na dzisiaj. Być może twórcy Kadu zrobią nam kiedyś porządny osobny moduł. Na razie pozostaje nam tylko ten sposób.


Nowy dysk :-]

Zmęczony ciągłym poszukiwaniem miejsca na dysku twardym zdecydowałem się kupić sobie dysk zewnętrzny. Wybór padł na Western Digital My Book Essential Edition o pojemności 1TB.  Zadecydowała marka firmy + przystępna cena 399 zł. Od razu zaznaczam, że nie jestem związany z żadnym producentem sprzętu ani nikt mi nie płacił za recenzję. Sprzęt kupiłem za własne pieniądze i dlatego mogę swobodnie pisać co myślę.

W pudełku mamy zasilacz sieciowy 100~240V z wymiennymi wtyczkami, kabel USB (ok. 1 m długości) + dysk twardy 3,5 cala zamknięty w ładnej obudowie (fotki oglądać np. u producenta).

Po uruchomieniu w Windowsie naszym oczom ukazuje się menu wyboru instalacji oprogramowania. Wśród dostępnych opcji mamy wykonanie kopii zapasowej softu na dysku. Zrobiłem to... a następnie sformatowałem cały dysk, bo domyślny system plików FAT32 wg mnie należało zmienić na NTFS. Pozostałe z dostępnych do instalacji aplikacji to demówki do wykonywania kopii zapasowych systemu. Nie instalowałem tego (bo i po co skoro na Linuksie nie będą działały? :-) )

W komentarzach dotyczących dysku znalazłem info, że pojemność tak naprawdę wynosi 935GB. Jest to prawda. Otwory wentylacyjne znajdują się u góry, z dołu i z tyłu. Na reklamach dysk stoi pionowo, ja wolałem jednak położyć go płasko. Dysk pracuje bardzo cicho. W porównaniu z moim wysłużonym dyskiem wewnętrznym to absolutna cisza :-D

Ubuntu nie ma żadnych problemów z odczytem i zapisem. U mnie prędkość zapisu wynosi ok 2,5-3.2 MB/s (stary, wysłużony Celeron 2.8 GHz + płyta główna Asus P4PE-X + dysk Szajsung ATA cholera wie jakiej prędkości :-P ).

Stosując zasadę ograniczonego zaufania przez ok. miesiąc będę trzymał tam mało ważne rzeczy.

Na produkt mamy 2 lata gwarancji.

Póki co, mogę śmiało powiedzieć, że Tajlandczycy zrobili kawał dobrego sprzętu :-)


3 wzorce projektowe w ok. 100 liniach kodu PHP

Ostatnio sporo czytam na temat programowania obiektowego. W niemal każdej książce, która wpadła mi w łapki, przykłady wzorców projektowych podane są w języku Java. Wyjątkiem jest Programowanie obiektowe w PHP 5 Hasina Haydera.

Do napisania postu skłoniły mnie pojawiające się gdzieniegdzie narzekania programistów na PHP, że jakoby ten język to zabawka, że nie da się w nim tworzyć w pełni obiektowego kodu itd. Dzisiaj zademonstruję działanie 3 wzorców projektowych na raz (Strategia, Obserwator, Łańcuch zobowiązań).

Chcemy zrobić coś takiego:

Mamy więc jeden obiekt, który zawiera w sobie dane na temat obiektów, które chce powiadamiać, że coś się u niego zmieniło. Tak działa typowy wzorzec obserwator. Ja to jednak zmodyfikowałem tak, że pierwszy obserwator w łańcuchu otrzymuje informację o zmianie stanu obiektu obserwowanego. Jeżeli umie sobie poradzić z przekazanym komunikatem - reaguje. Jeżeli ta informacja go zbytnio nie interesuje - przekazuje następnemu obiektowi w łańcuchu. Tak działa wzorzec łańcuch zobowiązań. Po co mi wzorzec strategia? Z czystego lenistwa :-) Po co pisać kilka rodzajów obserwatorów do testu, które reagują w inny sposób na komunikaty, skoro można implementować interfejs reagujący na różne komunikaty. Jeżeli będę chciał testować inny rodzaj komunikatów to napiszę sobię nową implementację interfejsu reagującego i tyle :-)

Bierzemy się więc do roboty. Najpierw tworzymy sobie 2 interfejsy. 1 dla wzorca obserwator, drugi dla łańcucha zobowiązań.

interface Obserwujacy {
    public function powiadomienie($komunikat);
}

interface Lancuch {
    public function zareaguj($komunikat);
}

Następnie tworzymy sobie jakiś obiekt obserwowany:

class Obserwowany {

    private $obserwatorzy = array();
    private $komunikat;

    public function addObserwujacy(Obserwujacy $o) {
        $this->obserwatorzy[] = $o;
    }

    public function setKomunikat($k) {
        $this->komunikat = $k;
        $this->powiadamiaj();
    }

    private function powiadamiaj() {
        foreach ($this->obserwatorzy as $obserwator) {
            $obserwator->powiadomienie($this->komunikat);
        }
    }
}

Obiekt ma zaledwie 3 metody. Pierwsza dodaje kolejnego obserwatora do listy, druga zmienia stan obiektu (wysyła komunikat wszystkim), trzecia dla każdego obserwatora wywołuje metodę powiadamiającą o zmianie stanu.

Następnie tworzymy klasę Obserwator, która reaguje na wszystkie komunikaty.

class Obserwator implements Obserwujacy , Lancuch {

    protected $reagujNa; // implements ReagujNa
    protected $nazwa;
    protected $lancuch;

    public function __construct($nazwa, Lancuch $l) {
        $this->reagujNa = new ReagujNaWszystko();
        $this->nazwa = $nazwa;
        $this->lancuch = $l;
    }

    public function powiadomienie($komunikat) {
        $this->zareaguj($komunikat);
    }

    public function zareaguj($komunikat) {
        if ($this->reagujNa->reaguj($komunikat)) {
            echo '<strong>' . $this->nazwa . '</strong> otrzymal komunikat ' . $komunikat . ' i zareagowal.<br />';
        }
        else {
            $this->lancuch->zareaguj($komunikat);
        }
    }
}

Zmienna $reagujNa implementuje interfejs ReagujNa, który stworzymy poniżej. Niestety w PHP nie da się wymusić określonego typu zmiennej - w tym lepsza jest Java. Jeżeli ktoś koniecznie chce, to może sobie sprawdzać czy dana zmienna rzeczywiście implementuje interfejs przed wywołaniem.

interface ReagujNa {
    public function reaguj($komunikat);
}

class ReagujNaWszystko implements ReagujNa {
    public function reaguj($komunikat) {
            return true;
    }
}

class ReagujNaLiterki implements ReagujNa {
    public function reaguj($komunikat) {
        if (is_string($komunikat)) {
            return true;
        }
        else {
            return false;
        }
    }
}

class ReagujNaNumerki implements ReagujNa {
    public function reaguj($komunikat) {
        if (is_numeric($komunikat)) {
            return true;
        }
        else {
            return false;
        }
    }
}

Mamy tutaj trzy klasy posiadające po jednej metodzie, które zwracają prawdę lub fałsz w zależności od podanego parametru. Dalej wypadałoby wykorzystać te klasy zmieniając zachowanie obiektu Obserwator.

class ObserwatorLiterek extends Obserwator {
    public function __construct($nazwa, Lancuch $l) {
        parent::__construct($nazwa,$l);
        $this->reagujNa = new ReagujNaLiterki();
    }
}

class ObserwatorNumerkow extends Obserwator {
    public function __construct($nazwa, Lancuch $l) {
        parent::__construct($nazwa,$l);
        $this->reagujNa = new ReagujNaNumerki();
    }
}

Na koniec potrzebne jest nam jeszcze ostatnie ogniwo łańcucha, nazwane przeze mnie Limiter. Cokolwiek limiter dostaje - wyrzuca na wyjściu i tyle.

class Limiter implements Lancuch {

    private $nazwa;

    public function __construct ($nazwa) {
        $this->nazwa = $nazwa;
    }
    public function zareaguj($komunikat) {
        echo '<strong>Limiter ' . $this->nazwa . '</strong> otrzymal komunikat ' . $komunikat . '<br />';
    }
}

Pora to wszystko przetestować. Tworzymy obiekt obserwowany,

$obserwowany = new Obserwowany();

a następnie 4 ciągi obserwatorów. Tworzymy go od końca. Coś jak Matrioszki.

// ciag 1
$limiter1 = new Limiter('ciagu 1');
$obserwatorWszystkiego = new Obserwator('Obserwator Wszystkiego ciagu 1',$limiter1);
$obserwowany->addObserwujacy($obserwatorWszystkiego);
// ciag 2
$limiter2 = new Limiter('ciagu 2');
$obserwatorLiterek = new ObserwatorLiterek('Obserwator Literek ciagu 2',$limiter2);
$obserwowany->addObserwujacy($obserwatorLiterek);
// ciag 3
$limiter3 = new Limiter('ciagu 3');
$obserwatorNumerkow = new ObserwatorNumerkow('Obserwator Numerkow ciagu 3',$limiter3);
$obserwowany->addObserwujacy($obserwatorNumerkow);
// ciag 4
$limiter4 = new Limiter('ciagu 4');
$obsAll = new Obserwator('Obserwator Wszystkiego ciagu 4',$limiter4);
$obsNum = new ObserwatorNumerkow('Obserwator Numerkow ciagu 4',$obsAll);
$obsLit = new ObserwatorLiterek('Obserwator Literek ciagu 4',$obsNum);
$obserwowany->addObserwujacy($obsLit);

Ostatnim krokiem jest sprawdzenie jak to teraz działa.

$obserwowany->setKomunikat('abc');
echo '----------------------------------';
$obserwowany->setKomunikat(123);

Wynik:
Obserwator Wszystkiego ciagu 1 otrzymal komunikat abc i zareagowal.
Obserwator Literek ciagu 2 otrzymal komunikat abc i zareagowal.
Limiter ciagu 3 otrzymal komunikat abc
Obserwator Literek ciagu 4 otrzymal komunikat abc i zareagowal.
----------------------------------
Obserwator Wszystkiego ciagu 1 otrzymal komunikat 123 i zareagowal.
Limiter ciagu 2 otrzymal komunikat 123
Obserwator Numerkow ciagu 3 otrzymal komunikat 123 i zareagowal.
Obserwator Numerkow ciagu 4 otrzymal komunikat 123 i zareagowal.

A teraz wyjaśnienie jak to wszystko działa:

Ciąg 1 zawiera w sobie tylko obserwatora wszystkiego i limiter. Obserwator każdy komunikat traktuje jako informację dla siebie i nie przekazuje dalej do limitera (przez co komunikat nigdy do niego nie dojdzie).

Ciąg 2 ma tylko obserwatora literek i limiter. Na pierwszy, słowny komunikat zareagował. Na komunikat zawierający liczbę nie zareagował i przekazał dalej do limitera.

Ciąg 3 ma w zamian obserwatora numerków i limiter. Zachował się dokładnie odwrotnie od poprzedniego.

Ciąg 4 ma kolejno obserwatorów literek, numerków, wszystkiego i limiter. Teraz dokładnie widać jak to działa. Jeżeli dany obserwator nie znalazł informacji przydatnej dla siebie - po prostu przekazał następnemu obserwatorowi w ciągu. Warto zauważyć, że obserwator wszystkiego i limiter w tym przypadku nigdy nie zareaguje, gdyż komunikat zostanie rozwiązany zanim do niego dojdzie.

Dla chętnych zamieszczam plik wzorce.php. Zachęcam do eksperymentów i ewentualnej przebudowy obserwatorów tak, aby reagowały na konkretne słowa lub liczby.


Jeszcze żyję, spokojnie :-)

Ostatnio nie miałem w ogóle czasu pisać. Blogasek trochę zdążył się zakurzyć. Od ostatniego miesiąca sporo się wydarzyło. Zmieniłem sobie stan cywilny. Aktualnie jestem w trakcie przeprowadzki. Nie ma nawet kiedy porządnie siąść za klawiaturę kompa, a co dopiero coś sensownego napisać...

Zamiast pisać głupoty przejdę do konkretów. Nigdy nie przypuszczałem, że organizacja ślubu i wesela aż tak wysysa życie z człowieka. Na każdym kroku telefony, umawianie się, spisywanie umów, zadatków, terminów i innych takich bzdur. Do tego jeszcze dochodzą "random events", czyli np. na 2 dni przed ślubem dzwoni do ciebie kierowca limuzyny i mówi, że maska limuzyny na skutek spotkania z drzewem skróciła się o 50 cm.

Następna kwestia to ceny, które ostatnio poszybowały w górę. Dobrze, że najważniejsze rzeczy związane z weselem były załatwiane na początku 2008 roku. Przed kryzysem, przed grypą i czym tam jeszcze mamy w 2009 roku. Ceny usług obowiązywały oczywiście stare.

Obiecałem laurkę szefowi restauracji, w której odbyła się impreza. Przyjęcie weselne odbyło się w Restauracji "Club 99" w sali tzw. afrykańskiej na terenie Międzynarodowych Targów Katowickich. Jedzenie smaczne, urozmaicone (części nawet nie umiem nazwać :-) ). Szefostwo zna się na organizacji tego typu imprez. Podają rozpiskę przykładowego menu i rozkład tego menu w czasie. Wszystko jest tak zaplanowane, że mamy jeszcze miejsce na główne posiłki. Mogą zorganizować ciasto i tort. Jeżeli mamy swoje - proszę bardzo. Żadnych opłat ukrytych typu "korkowe, prądowe, wodowe, kablowe, godzinowe" i inne takie. Płacimy od łebka ustaloną kwotę. Zabawa trwa do ostatniego gościa bez limitu czasu. Są tak pewni jakości swoich usług, że po imprezie odbytej w sobotę płacimy dopiero w poniedziałek. Fajnie, co? (z czymś takim się jeszcze nie spotkałem). Szefostwo cały czas jest na miejscu jakby co. Żeby nie było samego lukru napiszę jeszcze, że obsługa od godz. 2 w nocy nieco przystopowała. Część pustych naczyń leżało sobie na stole. Z drugiej strony, to nie roboty obsługują. Ludzie mają prawo być nieco zmęczeni, nieprawdaż?

Z zadań na najbliższy czas wyznaczyłem sobie zainstalować najnowsze Ubuntu, zobaczyć w kinie Terminatora :-P oraz jak zawsze pojawić się na kolejnej edycji Spodka 2.0 9 czerwca.