Kontener IOC

Dzisiejszy post będzie o moim własnonożnie zrobionym kontenerze IOC (inversion of control). Najpierw kilka słów, czym jest wzorzec IOC.

W wielkim uproszczeniu wzorzec IOC polega na przeniesieniu poza obiekt wszelkich funkcjonalności nie związanych bezpośrednio z jego przeznaczeniem. Celem jest:

  • zdefiniowanie jasnych odpowiedzialności poszczególnych klas
  • stworzeniem abstrakcji dzięki której zmiana jednego elementu systemu nie będzie wpływała na inne
  • uniezależnienie się od implementacji poszczególnych części systemu

Tyle do IOC, natomiast kontener IOC jest specjalną klasą, która na nasz zlecenie konstruuje za nas obiekty, których potrzebujemy dbając o wszelkie zależności między nimi.

Realizacja wygląda w ten sposób, że deklaratywnie określamy jakie są zależności między obiektami i na podstawie naszego opisu całą techniczną stroną tworzenia obiektów zajmuje się kontener IOC.

Kontener wraz z przykładem użycia można ściągnąć stąd (licencja BSD).

Mój kontener IOC składa się z dwóch podstawowych częsci:

  • fabryka obiektów
  • adaptery dla różnych formatów określania zależności między obiektami

Fabryka obiektów tworzy i zarządza obiektami biorąc pod uwagę czas ich istnienia. Zarządza obiektami, których stan trzymany jest przez całą sesję użytkownika jak i może ich stan być trzymany nieskończenie długo i dostępny między sesjami użytkowników ( do tego potrzebny jest mechanizm cache typu APC lub XCache z odpowiednim adapterem).

Co do adapterów to na dziś dzień stworzony jest jeden adapter, który czyta XML-e. Pokrótce o tym jak opisywać obiekty w tymże XML-u.


    
        
    
    
        
            baz_foo
        
    
          
        
            other_baz
            baz
        

        
            baz_faz
            baz_baz
        

        
            @self
        

        
            Foo1
            FooConst
            Foo2
            
        
       
        

Jak widać albo i nie ;). Stopień niżej od roota są trzy tagi – „application”, „session”, „request”, które grupują w sobie tagi object. Te trzy tagi określają czy stan obiektu będzie trwał dla sesji czy dla pojedynczego requestu czy wiecznie dla całej aplikacji.

Tagi object określają konfigurację poszczególnych obiektów. Kolejne właściwości obiektu ustawiamy za pomocą tagów „property”. Obowiązkowym atrybutem tagu „property” jest „name”, mówi on o nazwie parametru w konstruktorze lub nazwie settera przez który ma być wstrzyknięta właściwość obiektu. Dzięki temu rozwiązaniu, nie ma znaczenia w jakiej kolejności podamy właściwości, zawsze zostaną one właściwie wstrzyknięte.

Tag „property” może mieć też atrybut „type” określa on jak interpretować zawartość tagu. Jeżeli ma on wartość „class” lub „id” zostanie wstrzyknięta odpowiednia klasa. Jeżeli const, to zostanie wstrzyknięty string, który jest zależny od środowiska w którym działa aplikacja ( o tym jeszcze później ). Brak atrybutu „type” będzie oznaczał, że zostanie wstrzyknięte literalnie to co jest wewnątrz tagu.

Są dwa rodzaje tagów „object”. Jeden ma atrybut class a drugi class-match. Tagi z atrybutem class-match konfigurują obiekty po wyrażeniu regularnym. Tzn, że zamiast się powtarzać i pisać osobno dla każdej klasy np. tabeli, że ma być wstrzyknięty obiekt połączenia z bazą, można napisać tag z atrybutem class-match, który wszystkim klasom, które spełniają dane wyrażenie wstrzyknie odpowiednią zależność. Przypomina to w zasadzie funkcjonalność zmiennych statycznych klas, które zresztą też są obsługiwane.

Atrybutu „id” używamy gdy chcemy skonfigurować dwa obiekty tej samej klasy, żeby je rozróżnić pobieramy je właśnie po „id”.

Ostatnią rzecz, którą chciałbym powiedzieć w tym już przydługawym tekście jest tag „proxy”. Znajduje się on wewnątrz tagu „object” . Dzięki niemu, zamiast danego obiektu, zostanie zwrócona klasa typu proxy, która będzie mogła transparentnie dodać jakieś funkcjonalności do niego.

Zapraszam do pobierania kodu i zabawy nim. Na wszelkie pytania odpowiem w komentarzach ;).

Przykład użycia kontenera jest w zipie, w pliku index.php także nie będę się już rozpisywał jak php-ową część używać, mam nadzieje, że będzie wszystko jasne 😉

P.S nie napisałem jeszcze testów jednostkowych więc mogą być jakieś mniejsze czy większe zgrzyty. Dajcie znać jak coś 😉

  1. 1. ‚ściągnąć z tąd (licencja BSD)’ – stąd
    2. warto usprawnić kilka rzeczy:
    – przekazywanie argumentów do wywoływanych klas
    – możliwość wywołania metody fabrykującej
    3. czym jest @self?

  2. Wojciech Soczyński pisze:

    3. @self wstrzykuje nam samą instancję kontenera do danego obiektu.
    2. Po to jest kontener, żeby w nim konfigurować klasy. Idea jest taka, żeby zamiast fabrykować obiekty przekazując argumenty do wywoływanych klas opisać to deklaratywnie. Z tego też powodu metoda fabrykująca jest niepotrzebna. Wyciągamy z kontenera już skonfigurowane obiekty danej klasy. Jeżeli chcemy mieć inaczej skonfigurowany obiekt danej klasy, rozróżniamy go przez dodanie mu w XML-u atrybutu id. I wyciągnięciu po tymże id.

  3. A porównywałeś już swój kod np. z components.symfony-project.org ? Wg mnie Fabien dał radę, jeśli chodzi o ten komponent. Ciekawe są też jego uwagi nt. PHP 5.3 i tego, jak np. closures ułatwia pisanie kontenetów DI – slideshare.net.

  4. Wojciech Soczyński pisze:

    Nie porównywałem, ale generalnie z tego co czytałem na blogu Fabiena czy na innych blogach gdzie były wpisy opisujące ten komponent, to stoi za nim zupełnie inna filozofia.

    Co do domknięć i kontenera DI to czytałem ten wpis chyba na jego blogu i generalnie nie widzę sensu ich stosowania w tym przypadku. Ale jak mówię, mój kontener jest zupełnie inny.

  1. […] kontener IoC (inversion of control). Jest to maszynka, która tworzy nam i konfiguruje obiekty. Mój kontener IoC ma tą ciekawą właściwość, że pozwala zwracać zamiast żądanego obiektu jego proxy. Proxy […]

Leave a Reply

Informuj mnie o odpowiedziach poprzez e-mail. Możesz również subskrybować wpis bez zostawiania komentarza.