IoC[2] - Inversion of Control

16 Marca 2016

Zacząłem się wczytywać czym jest IoC. Okazuje się, że jest to bardzo szerokie pojęcie, a DI to tylko jedna jego interpretacja. W zeszłym tygodniu pisałem o tym czym jest Dependency Injection. Teraz natomiast opowiem pokrótce czym jest IoC i jak będę go używał w moim projekcie.

Inversion of Control

Jak nazwa wskazuje chcemy oddać część kontroli komuś/czemuś innemu. Jedną z możliwych interpretacji, w stosunku do GUI, jest zmiana sekwencyjnego pobierania danych od użytkownika na oddanie kontroli nad kolejnością wpisywania danych użytkownikowi. Innym przykładem są eventy, gdzie oddajemy kontrolę nad tym co będzie wykonywane, a decydujemy tylko o tym kiedy będzie wykonywane. Na końcu dochodzimy do DI, gdzie zostawiamy komuś innemu decyzję jaką implementacją danego interfejsu się posłużyć aby wykonać nasz kod.

No i fajnie. Dlaczego? Ponieważ pisanie klas w ten sposób powoduje, że możemy odłożyć w czasie decyzję jako coś zrobić. Dodatkowo dostajemy smaczki w postaci łatwiejszego (oraz dokładniejszego) testowania kodu.

No dobrze, ale tak sobie siedzę i myślę: kto powinien przerwać ten łańcuch interfejsów i zadecydować, że tej klasie damy taką implementację, która użyje tej implementacji, która ma jeszcze dwie zależności, które … i w tym momencie na scenę wchodzą kontenery.

IoC Container

Idea jest taka: mamy pojedynczą klasę, kontener, która agreguje implementacje interfejsów poprzez rejestrowanie ich, np:

container.Register<IInterface, IImplementation>();

A kiedy potrzebujemy użyć danej klasy, kontener utworzy nowy obiekt z zarejestrowanych informacji i automatycznie utworzy wszystkie niezbędne zależności (lub rzuci wyjątek jeśli czegoś nie zarejestrowaliśmy). Np:

var object = container.Resolve<IInteface>();

Gotowych kontenerów jest sporo, m.in. Microsoft Unity, Castle Windsor, Ninject, StructureMap, Autofac, i wiele innych. Ja zdecydowałem się na DryIoc.

Jeśli chcecie dowiedzieć się więcej na temat kontenerów, IoC, DI, to polecam ten artykuł.

Po co mi to wszytko?

Tak jak już pisałem, moim celem jest modularyzacja aplikacji, aby łatwo można było modyfikować poszczególne części. Dodatkowo wychodzę z założenia, że raz napisawszy bazę moich aplikacji, będę mógł tworzyć kolejne aplikacje bez konieczności modyfikowania tej bazy. Używając IoC jestem w stanie tego dokonać. Po uruchomienia pliku wykonywalnego, przeskanuje on dostępne biblioteki DLL w poszukiwaniu klas implementujących kluczowe interfejsy, zarejestruje je w kontenerze (oraz umożliwi im rejestrowanie swoich dodatkowych implementacji), a następnie zbuduje i uruchomi. Każda nowa aplikacja to będzie po prostu nowa biblioteka DLL, wrzucona do odpowiedniego folderu.