Po długiej przerwie cykl, wraz ze mną, wraca do życia. W poprzednim odcinku padło stwierdzenie, że rozwiązaniem problemu wielu skomplikowanych zależności jest kontener. I że najlepszy jest własny. O ile drugie stwierdzenie na pewno jest durne i bezsensowne, to z pierwszym też można polemizować.
A może tak bez kontenera?
Użycie kontenera jest jedną z opcji zarządzania zależnościami w projekcie… jednak nie jedyną. Pamiętacie “baobab” – analogię jaką w przebłysku geniuszu wysnułem o tutaj? Że zależności są jak matrioszka, że nie chciałbym składać swoich zależności ręcznie, i tak dalej…
Okazuje się, że to nie jest takie czarno-białe. Zapraszam do tekstu “Z kontenerem czy bez?” po więcej dywagacji.
Dlaczego nie “własny” kontener?
Na pewno nie będę produkcyjnie wykorzystywał czegoś, co sam napisałem, ponieważ jest to po prostu najgorsze z możliwych rozwiązań. Kontenery w prawdziwego zdarzenia mają tyle przewag nad moją implementacją, że długo by wymieniać… ale spójrzmy na kilka chociażby kwestii.
Po pierwsze: inny szanujący się kontener daje możliwość automatycznej rejestracji komponentów. Podaję mu dllki, które ma przeskanować, a on sam robi resztę. Mogę ten proces dodatkowo stuningować tak, żeby spełniał moje – czasem nawet dość wygórowane – wymagania, jednocześnie uciekając od konieczności każdorazowego dodawania linijki rejestrującej w kontenerze każdą nową klasę.
Po drugie: inny szanujący się kontener daje możliwość zarządzania cyklem życia tworzonych komponentów. Mój biedny pseudo-kontenerek ( https://github.com/maniserowicz/di-talk/blob/tests-for-container/src/app/PoorMansContainer.cs ) zawsze tworzył nową instancję obiektu. Jest to całkowicie poprawne DOMYŚLNE zachowanie, ale jako programista powinienem mieć możliwość powiedzenia mu: jeśli ktoś zażąda implementacji interfejsu X to w trakcie obsługi jednego webrequesta stwórz tylko jedną instancję. Albo daną “rejestrację” traktuj całkowicie jako singleton… co daje taką korzyść, że mam jedną instancję jakiejś klasy a ktoś czytający jej kod nie będzie na mnie krzyczał za to że wykorzystuję coś co często jest uznawane jako antipattern :).
Po trzecie: inny szanujący się kontener daje możliwość uzyskania całej kolekcji wszystkich implementacji danego interfejsu. Wystarczy jako zależność podać IEnumerable<IInterface>. Chociaż akurat to byłoby dość łatwo dodać i do mojego kontenerka.
Po czwarte: inny szanujący się kontener może automatycznie zarejestrować fabryki tworzące komponenty tak, abym mógł zadeklarować zależność jako Func<IInterface> i tylko w przypadku faktycznej konieczności utworzenia instancji uczynić to w mojej klasie “klienckiej”.
Po piąte: inny szanujący się kontener może pozwolić mi małym kosztem, bez wykorzystania dodatkowych narzędzi, zaimplementować AOP, czyli programowanie aspektowe! Co prawda ta koncepcja do mnie osobiście nie przemawia, ale warto zdawać sobie sprawę z takiej możliwości (o AOP będziecie mogli posłuchać przy okazji zbliżającego się eventu dotnetconf.pl, gdzie Basia będzie o tym gadać).
Po szóste: inny szanujący się kontener będzie prawdopodobnie działał szybciej niż ten mój. Ale do tego zaraz wrócimy.
Po siódme: inne kontenery oferują niekiedy bardzo ciekawe narzędzia wspomagające pracę z nimi. Zobaczcie chociażby jak wygląda debuggowanie Castle Windsor: http://docs.castleproject.org/Default.aspx?Page=Debugger-views&NS=Windsor. Jaw dropped? U mnie tak.
Po ósme: inne kontenery są pisane latami, przez dobre zespoły. Są przetestowane w setkach projektów przez tysiące programistów.
Po… i tak dalej.
Kontenery do wyboru
W świecie .NET mamy wybór naprawdę imponujący:
- Autofac
- Castle Windsor
- Ninject
- StructureMap
- Unity
- TinyIoC (dystrybuowany razem z Nancy)
- …i wiele innych
Ja od wielu lat na co dzień używam Autofac i z czystym sumieniem mogę go polecić.
W jednym projekcie pisanym w Nancy próbowałem używać TinyIoC i przez dość długi czas się sprawdzał, ale w końcu okazał się “zbyt tiny” i trzeba się było przemigrować na Autofaca.
Z 6 lat temu miałem romans z Unity, ale zrejterowałem. Jeśli ten projekt nie poszedł znacznie do przodu od tamtego czasu to zdecydowanie go nie polecam… ale jeśli by nie poszedł do przodu, to pewnie by już dawno zdechł. Więc pewnie poszedł. Więc nie mam opinii :).
Jak wybrać kontener?
W wielu “porównaniach” kontenerów na pierwszym miejscu badana jest ich wydajność. I jest to, powiem szczerze, jedna z najgłupszych rzeczy jaką można zrobić: porównywać kontenery pod względem wydajności i na tym opierać swój wybór. Dlaczego? Dlatego że dla mnie osobiście nie robi różnicy czy kontener jest w stanie stworzyć miliard obiektów z 5 czy 10 milisekund. Jeżeli wąskim gardłem w aplikacji jest proces tworzenia obiektów to pozostaje tylko pogratulować zespołowi, że tak znakomicie i megawydajnie ją napisał. Szapoba, buty cmokam. Ja się z taką aplikacją jeszcze nie spotkałem i wątpię, żebym się kiedykolwiek spotkał.
Na co zatem zwracać uwagę? Na możliwości kontenera oraz na proponowaną przez jego twórców składnię. Byłem niezmiernie zaskoczony gdy zaobserwowałem na jak wiele różnych sposobów można zrealizować dokładnie to samo zadanie, czyli zaimplementować parę funkcji Register/Resolve. Dodatkowych rekomendacji nie mam: hulaj dusza, piekła nie ma. Tym bardziej, że zmiana kontenera na inny nie powinna wiązać się z ogromnym nakładem pracy (chociaż raz – we wspomnianym projekcie w Nancy – tak się niestety zdarzyło i wpięcie Autofaca zajęło mi tydzień z okładem).