Skip to content

Commit a378c60

Browse files
committed
koin post added
1 parent 3814a8a commit a378c60

File tree

5 files changed

+224
-1
lines changed

5 files changed

+224
-1
lines changed

_drafts/2019-05-13-dagger.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ image: libraries/dagger
77
github: libraries/tree/master/dagger
88
description: "Biblioteki"
99
version: Dagger 2.21
10-
keywords: "dagger, wstrzykiwanie, zależności, moduł, komponent, podkomponent, depenendency, injection, di, inject, provides, module, component, subcomponent, scope, qualifier, activity, fragment, injector, androidinjectionmodule, android, programowanie, programming"
10+
keywords: "dagger, wstrzykiwanie, zależności, moduł, komponent, podkomponent, depenendency, injection, di, inject, provides, module, component, subcomponent, scope, qualifier, activity, fragment, injector, androidinjectionmodule, android, java, programowanie, programming"
1111
---
1212

1313
## Wstęp

_drafts/2019-05-20-koin.md

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
---
2+
layout: post
3+
title: "Koin"
4+
date: 2019-05-20
5+
categories: ["Biblioteki"]
6+
image: libraries/koin
7+
github: libraries/tree/master/koin
8+
description: "Biblioteki"
9+
version: Koin 2.0
10+
keywords: "koin, wstrzykiwanie, zależności, moduł, komponent, depenendency, injection, di, inject, module, component, scope, qualifier, activity, mockito, kotlin, android, programowanie, programming"
11+
---
12+
13+
## Wstęp
14+
`Wstrzykiwanie zależności` (`Dependency Injection`) niezależnie od sposobu realizacji jest nieodłącznym elementem procesu budowania większości aplikacji. Wykorzystanie dostawcy zależności realizuje paradygmat `Odwrócenia sterowania` (`Inversion of Control`), który przenosi odpowiedzialność za inicjalizację niektórych obiektów na rzecz kontenera. Pozwala zmniejszyć ilość często powtarzającego się nadmiarowego kodu (`boilerplate`) dzięki czemu tworzony kod staje się krótszy, czytelniejszy, łatwiejszy w utrzymaniu i testowaniu. `Koin` jest biblioteką wstrzykiwania zależności o lekkiej strukturze przeznaczoną przede wszystkim dla programistów `Kotlin`. W przeciwieństwie do innych popularnych rozwiązań (np. `Dagger`) nie generuje dodatkowego kodu oraz nie wykorzystuje proxy czy refleksji, a pragmatyczne API sprawia, że jego użycie i konfiguracja jest łatwa i zwięzła.
15+
16+
## Moduł
17+
Konfiguracja i inicjalizacja obiektów dla kontenera zależności odbywa się w instancji modułu (`module`) dzięki czemu nie jest wymagane tworzenie osobnej klasy dla każdego modułu. Aby utworzyć moduł należy przypisać do zmiennej rezultat funkcji module w której zwracane są instancje danych typów - funkcje `single` dla pojedynczej instancji w obrębie zakresu i funkcja `factory` dla zwyczajnej instancji.
18+
19+
{% highlight kotlin %}
20+
//create main module for whole app
21+
class App : Application() {
22+
23+
//create Koin module
24+
val appModule = module {
25+
26+
//single instance of something
27+
single { PreferenceManager.getDefaultSharedPreferences(get()) }
28+
29+
//provide names to recognize instances of the same type
30+
single(name = "red") { SimpleDependency("red", "oval") }
31+
single(name = "blue") { SimpleDependency("blue", "triangle") }
32+
33+
//get provided NetworkManager into ComplexDependency factory
34+
factory { ComplexDependency(get()) }
35+
single { NetworkManager("androidcode.pl", 80) }
36+
37+
//single instance of ParentDependency provided by ChildDependency
38+
single<ParentDependency> { ChildDependency("text", 100) }
39+
//for providing single instance matching multiple types use code below:
40+
//single { ChildDependency("text", 100) } bind ParentDependency::class
41+
}
42+
}
43+
{% endhighlight %}
44+
45+
## Rejestracja
46+
Tworzenie i rejestracja komponentu kontenera `KoinApplication` zachodzi przy wywołaniu funkcji `startKoin` w której dokonuje się konfiguracji i przekazania referencji do modułów. Tworzenie kontenera może odbywać się w dowolnej klasie Android jednakże warto zarejestrować go w głównej klasie aplikacji dzięki czemu stanie się dostępny globalnie.
47+
48+
{% highlight kotlin %}
49+
class App : Application() {
50+
51+
//create Koin module
52+
val appModule = module {
53+
//definition
54+
}
55+
56+
override fun onCreate() {
57+
super.onCreate()
58+
59+
//create and register KoinApplication instance
60+
startKoin {
61+
androidLogger()
62+
androidContext(this@App)
63+
modules(appModule)
64+
}
65+
}
66+
}
67+
{% endhighlight %}
68+
69+
## Wstrzykiwanie
70+
Wstrzykiwanie zależności może zostać zrealizowane przez leniwą inicjalizację członków klasy przy pomocy funkcji `inject` lub poprzez bezpośrednie wstrzyknięcie do instancji funkcją `get`.
71+
72+
{% highlight kotlin %}
73+
class MainActivity : AppCompatActivity() {
74+
75+
//lazy inject
76+
val simpleDependencyRed by inject<SimpleDependency>("red")
77+
val simpleDependencyBlue: SimpleDependency by inject("blue")
78+
val complexDependency: ComplexDependency by inject()
79+
80+
override fun onCreate(savedInstanceState: Bundle?) {
81+
super.onCreate(savedInstanceState)
82+
setContentView(R.layout.activity_main)
83+
84+
//inject by directly getting instance
85+
val pref: SharedPreferences = get()
86+
}
87+
}
88+
{% endhighlight %}
89+
90+
## Zakres
91+
Zakres pozwala na wykorzystanie wybranych zależności tylko w obrębie danej klasy, a jego tworzenie realizowane jest przy pomocy funkcji `scope` i oznaczania zależności definicją `scoped`. Wstrzyknięcie zależności odbywa się z poziomu obiektu `ScopeInstance` otrzymywanego z funkcji `getActivityScope` (`getFragmentScope` dla `Fragment`), którego należy powiązać z cyklem życia komponentu za pomocą `bindScope`.
92+
93+
{% highlight kotlin %}
94+
class App : Application() {
95+
96+
val appModule = module {
97+
//definitions
98+
}
99+
100+
val scopeModule = module {
101+
scope<ScopeActivity> {
102+
scoped("scoped") { SimpleDependency("black", "square") }
103+
}
104+
}
105+
106+
override fun onCreate() {
107+
super.onCreate()
108+
startKoin {
109+
androidLogger()
110+
androidContext(this@App)
111+
modules(appModule, scopeModule)
112+
}
113+
}
114+
}
115+
116+
class ScopeActivity : AppCompatActivity() {
117+
118+
//inject instance from current scope
119+
val simpleDependencyBlack: SimpleDependency by getActivityScope().inject()
120+
121+
override fun onCreate(savedInstanceState: Bundle?) {
122+
super.onCreate(savedInstanceState)
123+
setContentView(R.layout.activity_scope)
124+
125+
//bind scope session to lifecycle
126+
bindScope(getActivityScope())
127+
}
128+
}
129+
{% endhighlight %}
130+
131+
## ViewModel
132+
Koin oferuje także integracje z klasą `ViewModel` co znacząco ułatwia pracę w przypadku realizacji aplikacji z wykorzystaniem mechanizmu `data binding` dla widoków. Aby dostarczyć obiekt typu `ViewModel` należy go oznaczyć w module jako `viewModel` oraz pobrać w miejscu docelowym za pomocą funkcji `viewModel` dla leniwej inicjalizacji lub bezpośrednio zainicjalizować funkcją `getViewModel`.
133+
134+
{% highlight kotlin %}
135+
class App : Application() {
136+
137+
val appModule = module {
138+
//definitions
139+
}
140+
141+
val viewModelModule = module {
142+
single { SimpleDependency("white", "rectangle") }
143+
viewModel {
144+
SomeViewModel(get())
145+
}
146+
}
147+
148+
override fun onCreate() {
149+
super.onCreate()
150+
startKoin {
151+
androidLogger()
152+
androidContext(this@App)
153+
modules(appModule, viewModelModule)
154+
}
155+
}
156+
}
157+
158+
class ViewModelActivity : AppCompatActivity() {
159+
160+
//lazy inject ViewModel
161+
val viewModel: SomeViewModel by viewModel()
162+
163+
override fun onCreate(savedInstanceState: Bundle?) {
164+
super.onCreate(savedInstanceState)
165+
setContentView(R.layout.activity_viewmodel)
166+
167+
//inject by directly getting ViewModel instance
168+
val viewModel: SomeViewModel = getViewModel()
169+
}
170+
}
171+
{% endhighlight %}
172+
173+
## Testowanie
174+
Wstrzykiwanie zależności może zostać także wykorzystywane w procesie testowania z `JUnit` dokładnie w taki sam sposób jak w klasach kodu źródłowego. Klasa testowa powinna rozszerzać `KoinTest`. Dodatkowo Koin umożliwia również tworzenie atrap (`mock`) obiektów dzięki czemu możliwa jest współpraca z bibliotekami naiwnych implementacji (np. `Mockito`).
175+
176+
{% highlight kotlin %}
177+
class SimpleDependencyTest: KoinTest {
178+
179+
//define some test module dependencies
180+
val testModule = module {
181+
single { SimpleDependency("red", "oval") }
182+
}
183+
184+
//inject instance into member or get inside method test
185+
val dependency: SimpleDependency by inject()
186+
187+
//init test dependencies before each test
188+
@Before
189+
fun before() {
190+
startKoin {
191+
modules(testModule)
192+
}
193+
}
194+
195+
//stop test dependencies after each test
196+
@After
197+
fun after() {
198+
stopKoin()
199+
}
200+
201+
//test dependency
202+
@Test
203+
fun testChangeColorWithInjection() {
204+
assertEquals("SimpleDependency is red oval", dependency.getInfo())
205+
dependency.changeColor("orange")
206+
assertEquals("SimpleDependency is orange oval", dependency.getInfo())
207+
}
208+
209+
//test mock
210+
@Test
211+
fun testCheckMock() {
212+
//declare and get mock
213+
declareMock<SimpleDependency>(name = "mock")
214+
val mock: SimpleDependency = get(name = "mock")
215+
216+
//asserts, rules, verify
217+
assertNotNull(mock)
218+
Mockito.`when`(mock.getInfo()).thenReturn("SimpleDependency is green oval")
219+
assertEquals("SimpleDependency is green oval", mock.getInfo())
220+
Mockito.verify(mock, times(1)).getInfo()
221+
}
222+
}
223+
{% endhighlight %}
172 KB
Loading
9.99 KB
Loading
21.2 KB
Loading

0 commit comments

Comments
 (0)