Wstrzykiwanie zależności i kontener Windsor Castle

karton

Wstrzykiwanie zależności i biblioteka Windsor Castle.

Dzięki zastosowaniu tych dwóch rzeczy możemy naszą aplikacje przekształcić w kod prosty i zrozumiały poprzez zależności. Dodatkowo przy zastosowaniu kontenerów (Windsor Castle) dostajemy gotowy obiekt wraz ze wszystkimi przypisanymi mu zależnościami bez pisania zbędnego i powtarzającego się kodu. Możemy w łatwy sposób napisać testy do takiej aplikacji wspierając również reguły KISS i SOLID.

No dobrze, ale co to w ogóle jest Depencdency Injection i Windsor Castle?

 

Wstrzykiwanie zależności (Dependency Injection)

(Via Wikipedia)

Wstrzykiwanie zależności (ang. Dependency Injection, DI) – wzorzec projektowy i wzorzec architektury oprogramowania polegający na usuwaniu bezpośrednich zależności pomiędzy komponentami na rzecz architektury typu plug-in. Jest on często utożsamiany z odwróceniem sterowania (ang. Inversion of Control, IoC), jakkolwiek z technicznego punktu widzenia DI jest jedną ze szczególnych (obecnie najpopularniejszą) realizacji paradygmatu IoC.

Czym jest odwrócenie zależności (Inversion of Control,IoC) ?

Odwrócenie sterowania (ang. Inversion of Control, IoC) — paradygmat (czasami rozważany też jako wzorzec projektowy lub wzorzec architektury) polegający na przeniesieniu na zewnątrz komponentu (np. obiektu) odpowiedzialności za kontrolę wybranych czynności.

 

Koniec teorii.  Skupmy się na praktycznym przykładzie.

Wyobraźmy sobie pracodawcę i pracownika. Pracodawca powinien mieć dostęp do wszystkich swoich pracowników, a nie tylko do jednego. Dodatkowo każdy pracownik powinien mieć zdefiniowane zadania przez pracodawcę, a nie tylko jeden konkretny. Takie możliwości daje nam właśnie wstrzykiwanie zależności.

Aby prawidłowo zaimplementować podejście wstrzykiwanie zależności musimy napisać interefejsy dla naszego pracownika i pracodawcy tak aby zdefiniować co każdy pracownik i pracodawca mają robić.

Metoda WorkOn określa pracę jaką wykonuje pracownik, a metoda CommandForTheEmployee  z intefejsu pracodawcy będzie wydawała polecenia do wykonania przez pracownika.

Stwórzmy więc klasy pracownika i pracodawcy.

 

Klasa Boss implementuje interfejs IEmployer. Posiada prostą metodę CommandForTheEmployee która przekazuje informacje do metodyWorkOn z klasy pracownika.

Klasa młodszego programisty, która reprezentuje pracownika. Posiada metodę WorkOn która przyjmuje informacje w postaci stringa i „dokleja” go do zdania które „mówi” programista.

Jak widzimy w klasie Boss, w trzeciej linijce tworzymy obiekt programisty. Nie jest to jednak dobre rozwiązanie, ale dlaczego?

Stwórzmy klasę kolejnego pracownika tym razem SeniorProgrammer.

Jak widzimy jest ona prawie identyczna co JuniorProgrammer a różnica leży jedynie w stringu whoIAm.

Wyobraźmy sobie sytuację gdy tą samą czynność chcemy przekazać pracownikowi – starszemu programiście z klasy Boss. Możemy zaimplentować to tak jak zrobiliśmy w przypadku młodszego programisty:

I tutaj pojawia się problem – teraz w klasie musimy uwarunkować czy i kiedy chcemy aby któryś z pracowników wykonywał daną czynność ponieważ nie chcemy aby obydwoje wykonywali tą samą pracę jednocześnie. Będziemy musieli wtedy zapłacić za dane zlecenie dwóm pracownikom, a i tak zrobią tą samą rzecz – jest to nieopłacalne. Do tego łamiemy regułę KISS i SOLID.

Spróbujmy przerobić naszą klasę Boss:

Hmm.. Co tu się stało. Otóż wstrzykujemy tutaj interfejs do konstruktora (wstrzykiwanie zależności przez konstruktor). Dzięki temu możemy przekazać dowolną klasę, która implementuje po IEmployee przez to klasa Boss rozpozna którego pracownika jej dajemy. Unikamy pisania dziwnych uwarunkowań i redundancji kodu w aplikacji.

Możemy teraz przesyłać pracowników do klasy Boss, a „szef” będzie odwoływał się do konkretnego z nich.

Przejdźmy do utworzenia pracownika i pracodawcy oraz przekażmy polecenia zadane przez szefa pracownikowi.

wstrzykiwanie zależności

Mamy już naszą wstrzykniętą zależność która działa jak należy, jednak po co cały czas tworzyć nowe obiekty. Gdy chcemy wywołać szefa np. w innej klasie to znowu będziemy zmuszeni do tworzenia obiektów za pośrednictwem new. Aby tego uniknąć możemy zainstalować bibliotekę Castle.Windsor.

castle windsor

 

Castle Windsor jest kontenerem na IoC. Możemy zarejestrować tutaj nasze zależności. Nie musimy się martwić o tworzenie obiektów oraz o wspomnianych zależnościach. Przykład:

W pierwszej linijce tworzymy kontener na nasze zależności następnie je rejestrujemy (w skrócie – dajemy mu interfejs) a on nam daje klasę przypisaną do tego interfejsu w naszej metodzie kontenera Register.
Przykładowo:

Po wywołaniu metody Resolve otrzymujemy klasę Boss z referencją do pracownika z klasy JuniorProgrammer.

Teraz wystarczy wydać polecenie pracownikowi przez pracodawcę:

I tak o to zastosowaliśmy wstrzykiwanie zależności poprzez konstruktor. Dzięki temu aplikacja jest przejrzysta i prosta w użyciu. Gdy chcemy zmienić pracownika wystarczy zmienić w kontenerze bądź zaimplementować nowy kontener z innymi zależnościami. „Sky is the limit”.

Możemy jeszcze udoskonalić nasz kod – utworzyć klasę z naszym kontenerem tak, aby nasza aplikacja była bardziej przejrzysta tak aby można było skorzystać z naszego konteneru w całej naszej aplikacji (po co pisać coś dwa razy). Unikajmy redundancji.

Klasa z kontenerem:

Link do GitHubTUTAJ

Cały kod:

 

2 Comments on “Wstrzykiwanie zależności i kontener Windsor Castle”

  1. 2 problemy:
    – poważniejszy, używasz service locator który uważany jest za anty-wzorzec, polecam lekturę http://www.pzielinski.com/?p=1154
    – natury logicznej: trochę nie rozumiem zależności, gdzie boss przyjmuje pracownika. W takim rozwiązaniu musimy tworzyć osobnego bossa dla każdego pracownika, a chyba nie o to chodzi 😀

    1. -Jasne że powinno się przekazywać zależności przez kontruktor, w następnym wpisie postaram się uniknąć „pójścia na skróty”;)
      -Jest to jedynie przykład, na przyszłość spróbuje znaleźć bardziej „praktyczne” 😉

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *