Dependency Injection – Kontenery

Tak jak obiecałem, że zrobie kolejną część o kontenerach tak słowa dotrzymam 🙂 Oto kolejna część 🙂 Dzisiaj o bardzo przydatnych narzędziach, chociaż niektórzy myślą, że niepotrzebnych(ale to temat na inny artykuł), ale rozwiejemy w tym artykule te wątpliwości 🙂 Panie i Panowie poznajcie kontenery 🙂


Wstęp

W poprzednim artykule rozstaliśmy się w dużym stresie 🙂 Ale to był zamierzony efekt, jakże testy mogą utrudnić projekt… NIEPRAWDA!!! To wynika tylko przez niewiedzę. Mieliśmy wtedy do rozwiązania trzy kwestie.

  1. Wielkie konstruktory – gdybyśmy mieli załóżmy 10 zależności w klasie GameServer a w każdej tej zależności kolejne zależności to wyszłoby by z tego niezłe bagno nie dość, że konstruktory byłyby kompletnie nieczytelne to musielibyśmy to wszystko pisać z palca, no mnie nie chciałoby się tego robić…
  2. Chcieliśmy również usunąć new nawet z poziomu klienta co również ułatwia nam utrzymywanie projektu wiemy z ostaniej zasady SOLID im mniej new tym lepiej 🙂
  3. No i ostatnia kwestia związana z punktem drugiem, najważniejsza. Chcemy, żeby projekt się kompilował nawet, jeśli nie mamy zaimplementowanego interfejsu. A żeby projekt się kompilował musimy usunąć new. My chcemy pisać testy a nie uruchamiać projekt, ale po co pisać testy jak one się nam nawet nie odpalą? Troche lipa…

Wszystkie powyższe problemy rozwiązują kontenery, ale najpierw wyjaśnijmy sobie co to w ogóle są kontenery.

Jak to Maciek z bloga devstyle.pl napisał o kontenerach https://devstyle.pl/2014/06/26/di-kontener/ (naprawde szczerze polecam również obowiązkowo przeczytać co Maciek napisał o kontenerach), że to są takie matrioszki, czyli, baby w babach 🙂

Z zależnościami w klasach jest podobnie jak z matrioszkami, można pisać te zależności z palca, ALE jest to cholernie nudne, jeśli klasa ma zależności a w tych zależnościach są kolejne zależności.

Można sobie wyobrazić jakiś serwis, który używa kontrolerów, pobiera dane z bazy, musi obsłużyć wiele różnych żądań i tak dalej… Nie polecam pisać tego wszystko z palca… są lepsze sposoby.

A w skrócie kontener to po prostu jakaś biblioteka z gotowymi funkcjami do implementacji dependency injection, o tak, żeby życie ułatwić 🙂

Bardzo polecam używanie kontenerów rozwiązują wiele problemów o czym zaraz się przekonacie 🙂

A jakiego kontenera my użyjemy? Jest ich pełno(najważniejsze z nich opisze w następnym artykule), ale dzisiaj napiszemy sobie własny kontener, żeby lepiej zrozumieć idee kontenerów.

W realnym świecie stanowczo odradzam pisanie własnego kontenera do dużych projektów, ale my sobie zrobimy w ramach nauki, nasz kontener będzie robił te najprostsze rzeczy wymagane tylko do rejestracji komponentów.

A jakie są główne zadania kontenera? Są dwa 🙂 rejestracja klasy, albo jej interfejsu, żeby kontener o niej wiedział, że taka klasa w ogóle jest w projekcie oraz utworzenie instancji tej klasy.


Testowanie kontenera

Wiadomo, że Boromir ma rację 🙂 Więc zobaczmy jak wygląda taki najprostszy możliwy kontener, jak wiemy testy jednostkowe to można powiedzieć taka dokumentacja projektu, zobaczmy najpierw testy sprawdzające czy klasy rejestrują się jak należy 🙂

Wiadomo do tych testów utworzyłem jakieś klasy bez logiki, ale jak te klasy są w testach to te klasy nie muszą posiadać żadnej logiki 🙂

Jak widać w testach najpierw sprawdzamy, czy ten kontener rejestruje klasy tak jak się należy. Teraz zobaczmy testy, które również sprawdzają kiedy kontener nie rejestruje klas, jeśli czegoś brakuje wyrzucamy odpowiedni wyjątek 🙂

Oto one:

Te testy, które wyrzucają wyjątki mają te wyjątki niezbyt trafnie opisane i nie pasują za bardzo do opisania problemu i nie tylko z trafnością nazw jest problem, gdy przejdziemy do kodu produkcyjnego kontenera trochę ulepszymy te testy 🙂 Ale to bedzie w punkcie „Dodatki do kontenera” na końcu artykułu.


Kontenerze… Do czego możesz mi się przydać?

Więc po co są kontenery? Pięć minut w tą czy w tą nic nie zmieni, lepiej nie komplikować sobie życia…

Nie do końca to jest prawda. 🙂 Zaraz wszystko stanie się jasne 🙂 Ale najpierw zobaczmy jak wygląda kod produkcyjny naszego kontenera. A ooo tak 🙂

Jak widać nie ma tu nic nadzwyczajnego. Możemy tym kontenerem jedynie rejestrować samą klasę lub klasę z interfejsem i tworzyć instancje zarejestrowanych klas. Jak można wywnioskowac z kodu metoda Reslove() tworzy nam instancje klasy, którą wcześniej zarejestrowaliśmy w kontenerze.

Naturalnie zdaje sobie sprawę, że można tu się przyczepić do miliarda rzeczy. To prawda nasz kontener nie ma wielu rzeczy, które mają normalne kontenery, ale jak już poprzednio pisałem ten kontener jest tylko do celów demonstracyjnch 🙂

Okej jak wszystko zrozumiane to zobaczmy co daje nam posiadanie takiego kontenera. Jaki był nasz cel? Żeby uruchomić testy bez implementacji interfejsu ICharacterRace, żeby konstruktor był bardziej czytelny i żeby pozbyć się new nawet z poziomu klienta.

Więc zobaczmy zmiany, które nas do tego celu doprowadzą. Zmieniły się jedynie klasy GameServer i WebServer, czyli klient.

Zobaczmy najpierw klasę GameServer

Jedyna zmiana jest taka, że zamiast konstruktora mamy coś takiego:

Można powiedzieć, że metoda ResolveInterfaces() to taki nasz konstruktor, ale jak widać o wiele bardziej czytelny 🙂

A w testach do klasy GameServer tak samo rejestrujemy zależności jak normalnie w funkcji Main() 🙂

A jakie zmiany są w klasie WebServer? Dodaliśmy jedną metodę i zmieniliśmy główną funkcje Main()

Metodę Shutdown() dodałem jedynie dla demonstracji, że gdybyśmy w dużym projekcie używali kontenerów to musielibyśmy czyścić nasz kontener, żeby nie posiadał cały czas instancji klas, które stworzył co by strasznie pamięć zżerało i nie tylko.

A co się stało w funkcji Main()? Rejestrujemy interfejsy naszych klas z których korzystamy, tworzymy instancję klasy GameServer i wywołujemy metodę ResloveInterfaces() oczywiście dalej nie mamy zaimplementowanego interfejsu ICharacterRace, więc gdy to uruchomimy to wybuchnie i tylko popiół zostanie 🙂 Ale jeśli się kompiluje to co nas to obchodzi? Możemy dalej implementować testy a projektu nie chcemy uruchamiać.

Warto wspomnieć, że istnieje również jak zasada three calls patterns czyli mówi ona o tym, że powinniśmy korzystać z kontenera jedynie w trzech miejscach przy jego starcie, na końcu oraz podczas przekazania sterowania do aplikacji(zwykle jest to tworzenie obiektów klas potrzebnych do działania aplikacji), czyli idea jest raczej bardzo prosta, niektórzy też zwą ten wzorzec RRR, czyli Register Resolve Release.

Bardzo polecam przeczytać również krótki artykuł Marka Seemanna na temat RRR innymi słowy three class patterns https://blog.ploeh.dk/2010/09/29/TheRegisterResolveReleasepattern/


Dodatki do kontenera

W ramach dodatku do naszego kontenera dodamy sobie rejestrowanie obiektów jako singletony, inne kontenery jak np Autofac czy Unity też mają tą funkcjonalność, dodałem również dodatkowe testy i ulepszyłem te o których mówiliśmy wcześniej 🙂

Zacznijmy od testów. Podzieliłem testy na klasy, żeby było wiadomo po nazwie klasy co testują i dla czytelności.

Żeby w każdej klasie po kolei nie tworzyć za każdym razem obiektu naszego kontenera, dodałem na początku testów coś takiego:

Metoda BeforeEach() wykonuje się przed każdym testem a AfterEach() jak sama nazwa mówi, po każdym teście.

Ogólnie mamy cztery klasy z testami. Zacznijmy od pierwszej ContainerTest

W skrócie w tej klasie sprawdzamy czy wszystkie klasy, zależności rejestrują się tak jak należy.

Jeszcze jedno do tych testów jest potrzebne tak samo jak do poprzednich testów mamy przykładowe klasy, niektóre puste niektóre z jakimiś randomowymi zależnościami. Do wszystkich tych testów ich używamy. Oto one 🙂 Hyc 🙂

Za ten czas przejdźmy do kolejnej klasy z testami. ContainerResolveTest Ciach 🙂

Tu również sprawdzamy czy wszystko rejestruje się jak należy, ale głównie sprawdzamy czy typy klas się zgadzają oraz sprawdzamy czy następuje poprawne rejestrowanie obiektów za pomocą genericsów 🙂

Zobaczmy klasę testującą singletona. Jest tam tylko jeden test, klasa nazywa się SingletonContainerRegisterTest a test wygląda tak:

Jak widać też sprawdzamy czy rejestrowanie klasy singletona odbywa się jak należy.

Teraz zobaczmy klasę wyrzucającą wyjątki, czyli to samo co wcześniej w artykule co obiecałem, że ulepszymy. To, żeby te wyjątki jak coś pójdzie nie tak, żeby wyrzucało jak należy

Teraz wygląda to już lepiej, ale tą klasę TypeNotRegisteredException musieliśmy sami w kontenerze utworzyć, żeby działało z sensem.

To tyle, jeśli chodzi o testy, przejdźmy w końcu do klasy kontenera zobaczmy co tam się zmieniło.

Zmieniliśmy jedynie metodę Resolve() i dodaliśmy rejestrowanie obiektów jako singleton.

To w sumie prawie cały kod, jeśli chodzi o rejestrowanie singletonów. No właśnie prawie musimy jeszcze dodać jedną linijkę na górze klasy:

Musimy mieć gdzie przechowywać obiekty singletonów o delegacie Func może będzie oddzielny artykuł, w skrócie jest to delegata w której można określić parametry typów wejściowych i wyjściowych na początek ten artykuł jest fajny 🙂https://www.tutorialsteacher.com/csharp/csharp-func-delegate

Zobaczmy teraz metodę Resolve().

W pierwszym if-ie sprawdzamy czy przekazany typ klasy jest już przekazany jak singleton, jeśli tak to zwracamy tą klasę jako singleton, jeśli nie to sprawdzamy czy przekazana klasa w ogóle jest zarejestrowana, jeśli nie, wyrzucamy wyjątek i przekazujemy go do klasy TypeNotRegisteredException, którą zaraz pokaże.

Dalej, jeśli klasa jest zarejestrowana to sprawdzamy czy zarejestrowana klasa posiada jakieś zależności, jeśli tak to tworzymy instancje tych zależności. Jeszcze klasy wyrzucające wyjątek, wyglądają one tak:

I to chyba wszystko, jeśli chodzi o zmiany.

W ramach testów utworzyłem sobie obiekt singletona i dodałem w tworzonym obiekcie metodę CountNumberOfCalling(), która zlicza wywołania tej metody w funkcji Main(). Tak to wygląda

Metoda CountNumberOfCalling()

Jeśli to singleton to za drugim razem w komunikacie powinno nam się wyświetlić 2.

Jak widać tak też się dzieje 🙂

Uff dobra to chyba wszystko niby tylko dodaliśmy trochę testów i rejestrowanie singletonów a jednak trochę kodu jest 🙂 Oczywiście link z całym kodem do githuba w podsumowaniu 🙂


Podsumowanie

W następnym artykule też bedzie o kontenerach, ale już tych profesjonalnych dokładniej opiszę co one potrafią i co mają czego nie ma nasz kontener, do zobaczenia 🙂

Link do githuba ze wszystkimi przykładami: https://github.com/Slaw145/DependencyInjectionContainers

Standardowo, przypominam o newsletterze, którym wysyłam powiadomienia o nowych wpisach oraz dodatkowe informacje na temat, ogółem mówiąc, świecie IT.

KONIECZNIE dołącz do społeczności DevmanCommunity na fb, części społeczności jest w jednym miejscu

-strona na fb: Devman.pl-Sławomir Kowalski

-grupa na fb: DevmanCommunity

Pytaj, komentuj pod spodem na końcu wpisu, podziel się nim, oceń go, rób co wolisz  🙂

Do następnego! Do zobaczenia!

 
Jeśli ten wpis ci się przydał podziel się nim ze swoimi znajomymi :)

Post a comment

avatar
  Subscribe  
Notify about