前言

最近的專案都會導入 Koin 但是大部分都是看了一下別人專案怎麼使用。 然後就在自己的專案內胡搞瞎搞一通,是時候好好來看一下官方文件怎麼說了。 官方文件 - Definitions

Writing a module

A Koin module is the space to declare all your components.

val myModule = module {
  // your dependencies here
}

官網定義的很清楚 module 就是放 dependencies 的地方。 所以你其實也可以把所有的東西都寫在一起。但通常我們不會這麼做。

Defining a singleton, factory

A singleton component means that Koin container will keep a unique instance of your declared component.

class MyService()

val myModule = module {
  single { MyService() }
}

A factory component declaration is a definition that will gives you a new instance each time you ask for this definition.

class MyService()

val myModule = module {
  factory { MyService() }
}

這裡需要特別注意的就是 singleton, factory 的定義不同。 如果你需要每次取得的實例都不同時,記得使用 factory 。

Resolving & injecting dependencies

class Service()
class Controller(val view: View)

val myModule = module {
  single { Service() }
  single { Controller(get()) }
}

只要你的 class 是使用建構子(constructor)的方式來注入其他依賴。 那只要用 get() Koin 就會自己幫你找到相符的 type 去引入囉。

Definition: binding an interface

interface Service{
  fun doSomething()
}

class ServiceImp(): Service{
  override fun doSomething() { //... }
}

val myModule = module {

    // 請注意,這樣取得的 Type 會是 ServiceImp
    single { ServiceImp() }

    // 請注意,這樣取得的 Type 會是 Service
    single { ServiceImp() as Service } // 方式一
    single<Service> { ServiceImp() }   // 方式二(推薦做法)

}

// 小孩子才選擇,我全都要。
val myModule = module {
    // 請注意,這樣會取得的 Type 會是 Service & ServiceImp
    single { ServiceImp() } bind Service::class
}

interface 的使用也是相當常見,注意 Type 的宣告即可。

Definition: naming & default bindings

既然我們可以使用 interface 那勢必會遇到一個問題是:相同的 interface 不同的實作,該怎麼區別呢?

val myModule = module {
    single<Service> { ServiceImpl1() }
    single<Service>(named("test")) { ServiceImpl2() }
}

val service : Service by inject(qualifier = named("default"))

我們可以透過 named 這個 qualifier 來區別實例。以上面的例子來說,如果沒有使用 named 。 你將會取得 ServiceImpl1 。

Declaring injection parameters

有時候,你在做 inject 的時候 parameters 並不是直接在 module 裡 declared 。 所以你可以這樣做

class Presenter(val view : View)

val myModule = module {
    single{ (view : View) -> Presenter(view) }
    // single, factory, scope 都可以這樣使用
}

val view = View()
val presenter : Presenter by inject { parametersOf(view) }

Dealing with generics

Koin 目前不支援使用 generics type ,所以我們要透過 named 來取得我們想要拿到的特定 Type 。

module {
    single(named("Ints")) { ArrayList<Int>() }
    single(named("Strings")) { ArrayList<String>() }
}