Koin bez tajemnic – tworzenie modułu i deklarowanie zależności

W tym wpisie przyjrzymy się bliżej podstawowym elementom biblioteki Koin. Po przeczytaniu tego materiału będziesz w stanie:

  • stworzyć moduł
  • zadeklarować zależność z użyciem factory
  • zadeklarować zależność z użyciem single – singleton
  • pobrać zależność przy pomocy get
  • uruchomić Koina

Ten post jest kontynuacją serii “Koin bez tajemnic”. Jeśli nie czytałeś poprzednich artykułów to poniżej znajdziesz do nich odnośniki.

  1. Wprowadzenie do dependency injection
  2. Tworzenie modułu i deklarowanie zależności (aktualnie czytany)

Tworzenie modułu

Moduł w bibliotece Koin możemy rozumieć jako element, który decyduje o tym, w jaki sposób wstrzykiwane są zależności. W obrębie jednej aplikacji jesteśmy w stanie deklarować dowolną ich liczbę. Dobrą praktyką jest tworzenie modułów, które w logiczny sposób grupują znajdujące się wewnątrz części składowe. Gdy pracujemy nad ekranem logowania możemy zadeklarować wszystkie zależności w module o nazwie loginModule. Jeśli natomiast wiemy, że niektóre obiekty będą używane w obrębie całej aplikacji możemy stworzyć moduł dedykowany do takich zależności – appModule.

Jednym ze sposobów na stworzenie nowego modułu jest w pierwszej kolejności zdefiniowanie zmiennej, która będzie przechowywać do niego referencję. W następnym kroku możemy wywołać funkcję module do zadeklarowania zależności. Spójrzmy na na przykład:

Widzimy, że do finalnej zmiennej appModule został przypisany moduł. Najtrudniejsze w tym całym procesie wydaje się (jak to zazwyczaj bywa) wymyślenie odpowiedniej nazwy, bo reszta jest prosta i intuicyjna. Przestrzeń między wąsatymi nawiasami może zostać teraz wypełniona deklaracjami potrzebnych zależności.

Deklarowanie zależności

Deklarowanie zależności przy użyciu biblioteki Koin może zostać zrealizowane na kilka sposobów. Podstawowymi funkcjami, które do tego służą, są factory oraz single. To co je od siebie odróżnia, to sposób w jaki tworzą obiekty. Czym w takim razie charakteryzuje się każda z wymienionych metod?

Factory

Obiekty zadeklarowane przy pomocy funkcji factory będą tworzone na nowo dla każdej klasy, która z nich korzysta. Jak to wygląda w praktyce? To tak jakbyśmy za każdym razem wywoływali konstruktor do stworzenia potrzebnego obiektu. Dzięki temu, że korzystamy z narzędzia do wstrzykiwania zależności nie musimy tego robić ręcznie tylko ta odpowiedzialność leży po stronie biblioteki.

Single

Jak sama nazwa sugeruje, użycie funkcji single przy deklarowaniu zależności będzie skutkować tym, że zostanie utworzona tylko jedna instancja obiektu danej klasy. Czy wiesz już z jakim wzorcem projektowym mamy tutaj do czynienia? Zgadza się, zadeklarowane w ten sposób obiekty będą singletonem. Nie musimy tworzyć implementacji tego wzorca ręcznie, biblioteka zadba o to samodzielnie.

Pobieranie zależności

Podczas uruchomienia biblioteki Koin (o tym jak to zrobić dowiesz się w jednym z kolejnych rozdziałów) tworzy się tzw. drzewo zależności, które jest odzwierciedleniem tego, co zostało wcześniej zadeklarowane w modułach. Jak się pewnie domyślasz sama definicja zależności nie wystarczy do poprawnego działania aplikacji, trzeba jeszcze gdzieś z tych zależności skorzystać.

Get

Funkcja get służy do tego, żeby pobrać odpowiednią zależność z drzewa i umieścić ją w miejscu jej użycia. Jej działanie jest natychmiastowe tzn., że w momencie wywołania od razu zwraca obiekt konkretnego typu (o ile znajdzie go w drzewie). Głównie stosuje się ją w modułach w celu pobrania obiektu, który jest potrzebny do stworzenia innej zależności.

Innymi funkcjami do pobrania zależności są inject oraz bind ale zostaną one szczerzej omówione w kolejnych wpisach. Warto w tym momencie wspomnieć, że gdy którakolwiek z wyżej wymienionych funkcji nie znajdzie deklaracji z konkretnym typem, to będzie to skutkowało crashem aplikacji.

Przykład użycia

Połączmy ze sobą zdobytą do tej pory wiedzę i przedstawmy powyższe sposoby wstrzykiwania zależności na przykładowym module. Do prezentacji posłużą nam następujące klasy i interfejsy:

  • Interfejs Api służący do komunikacji z zewnętrznym RESTowym serwerem.
  • Klasa UserRepositoryImpl, która implementuje interfejs UserRepository.
  • Dwie klasy Use Case. Jedna do logowania – LoginUseCase, a druga do rejestracji użytkownika – RegisterUseCase. Zauważ, że jako argument jest tam przekazywany interfejs repozytorium, a nie konkretna implementacja.

Jak zatem będzie wyglądał moduł dla powyższych elementów aplikacji? Zacznijmy może od zdefiniowania zależności dla warstwy sieciowej.

Widzimy, że przy użyciu pomocniczych funkcji zostały zdefiniowane dwie zależności. Obie z nich zostały oznaczone jako single, czyli w rezultacie tylko jedna instancja każdego z tych obiektów powstanie podczas działania aplikacji. Często warstwa sieciowa jest współdzielona między innymi komponentami aplikacji dlatego zaprojektowanie modułu w ten sposób ma swoje uzasadnienie.

Warto też zwrócić uwagę na użycie funkcji get, która została przekazana jako argument do innej funkcji tworzącej konkretną zależność – provideApi. Z racji tego, że obiekt typu Retrofit został już zadeklarowany wcześniej, to istnieje możliwość odszukania go w drzewie zależności i użycia w tym miejscu.

Przejdźmy do dalszej części przykładu. Zostały nam jeszcze do zdefiniowania klasy służące do logowania i rejestracji. Z racji tego, że obie operacje dotyczą użytkownika nasz moduł będzie nosił nazwę userModule.

Widzimy, że tym razem zależności zostały zadeklarowane przy pomocy funkcji factory. Oznacza to, że w każdym miejscu, gdzie będzie potrzeba ich użycia, zostaną stworzone nowe instancje obiektów tych klas. Sama struktura zdefiniowania takiej zależności jest bliźniaczo podobna do tej, którą poznaliśmy w przypadku single.

W powyższym module istnieje jednak jedna deklaracja, która nieco różni się swoją budową od pozostałych – UserRepositoryImpl. Zapis, który widzisz w przykładzie oznacza, że wymuszamy, aby klasa ta została zadeklarowana jako interfejs UserRepository. Możemy tak zrobić, bo jak dobrze pamiętasz klasa UserRepositoryImpl implementowała ten właśnie interfejs. W taki właśnie sposób, możemy przypisywać interesujące nas implementacje do konkretnego interfejsu korzystając z modułów w Koinie.

Uruchomienie Koina

Do uruchomienia Koina służy funkcja startKoin i powinna ona być wywołana z poziomu klasy Application.

Obowiązkowym elementem, który musi znaleźć się między wąsatymi nawiasami jest funkcja modules przy pomocy której wskazujemy, które moduły mają zostać użyte do stworzenia drzewa zależności. Kolejnym istotnym elementem jest przekazanie context’u aplikacji. Jest to możliwe dzięki funkcji androidContext. Wywołując później tę samą funkcję z poziomu modułu, możemy przekazać context aplikacji w miejsca, gdzie istnieje taka potrzeba. Jeśli chcemy, aby w konsoli wyświetlały nam się logi z biblioteki, to możemy taką opcję odblokować używając androidLogger. Pamiętaj tylko, że ze względów bezpieczeństwa nie powinno się publikować logów w releasowych wersjach aplikacji.

Ciąg dalszy

Jak wspomniałem na początku ten artykuł jest elementem serii “Koin bez tajemnic”. Kolejne artykuły pojawią się wkrótce. Jeśli wpis Ci się podobał, to będzie mi niezwykle miło jak udostępnisz go w mediach społecznościowych.

.
Tagi:

1 myśl na “Koin bez tajemnic – tworzenie modułu i deklarowanie zależności”

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *