Menggunakan Koin sebagai DI pada Android MVP Pattern

Ian Christian Adolfian Damping
5 min readJan 18, 2022

--

https://insert-koin.io/img/koin_2.0.png

Pada kesempatan kali ini saya akan menjelaskan secara singkat bagaimana cara saya menggunakan library library DI (Dependency Injection) yang populer untuk melakukan injection pada Android MVP Pattern.

Struktur MVP Pattern

Karena menurut saya injection menggunakan library library populer seperti Koin, Hilt dan Dagger 2 pada MVP Pattern termasuk sulit.

Sebelum nya mari kita lihat dulu bagaimana struktrur MVP Pattern di Android

class MainActivity : AppCompatActivity(),MainView {

private val presenter: MainPresenter by inject {
parametersOf(this)
}
// rest of the code
}

class MainPresenter(private val mView: MainView) {

fun logMe(name:String) {
mView.logMessage("hello: $name , how are you ?")
}
}

interface MainView {

fun logMessage(name:String)
}

Nah alurnya adalah MainPresenter Dependent (membutuhkan/bergantung) MainView dan MainActivity perlu implement MainView agar bisa menggunakan MainPresenter.

Dependency Injection

Apa itu Dependency Injection ? Dependency Injection menurut saya adalah suatu teknik / cara bagaimana membuat suatu class yang membutuhkan class lain agar dapat berjalan.

Mari kita lihat dua contoh penerapan tanpa DI dan menggunakan DI dari Android Official Documentation :

Tanpa DI

class Car {

private val engine = Engine()

fun start() {
engine.start()
}
}

fun main(args: Array) {
val car = Car()
car.start()
}

Tanpa menggunakan DI class Car akan membuat class Engine nya sendiri, kekurangan nya adalah class Car menjadi tightly coupled dan tidak fleksibel bila ingin memilih class Engine lainnya. Begitu juga bila terjadi masalah pada class Car kita tidak tau mana yang bermasalah apakah class Car itu sendiri atau kah class Engine ?

Menggunakan DI

class Car(private val engine: Engine) {
fun start() {
engine.start()
}
}

fun main(args: Array) {
val engine = Engine()
val car = Car(engine)
car.start()
}

Nah bila menggunakan DI class Car tidak membuat sendiri class Engine nya melainkan hanya menerima melalui constructor sehingga memberikan fleksibilitas bila ingin memilih class Engine lainnya. Begitu juga bila terjadi masalah kita bisa mengetahui mana yang bermasalah apakah class Car atau kah class Engine itu sendiri

Dependency Injection dengan Koin

Apa itu Koin ? Koin merupakan salah satu library populer yang di gunakan dalam mempermudah Android Developer dalam membuat sebuah class menggunakan teknik DI

Bagaimana cara menggunakan nya ?

Cukup mudah kita perlu membuat module & mendefinisikan component nya, yuk kita lihat satu persatu caranya

  1. Membuat Module
val myModule = module {
// your dependencies here
}

2. Definisikan Component

Component dari Koin ada 3 yaitu single, factory & scoped berikut penjelasannya :

  • single : kita akan membuat sebuah singleton class
  • factory : kita akan membuat sebuah class baru setiap kali digunakan
  • scoped : kita akan membuat sebuah class yang lifetime nya mengikuti sebuah scope

contoh implementasinya akan seperti ini :

// Car <- Engine
class Engine()
class Car(val engine : Engine)

val myModule = module {

// declare Engine as single instance
single { Engine() }
// declare Car as single instance
// resolving Engine instance with get()
single { Car(get()) }
}
// using Car
val car:Car by inject()

Cukup mudah di mengerti ya, jadi ketika kita ingin menggunakan sebuah class Car yang membutuhkan class Engine tanpa perlu membuat nya secara manual kita tinggal membuat mereka menggunakan Koin.

Koin pada MVP Pattern

Nah setelah kita mengetahui struktur dari MVP Pattern , Konsep DI & menggunakan Koin. ada 2 cara bagaimana melakukan DI pada MVP Pattern menggunakan Koin yaitu :

  • Binding an Interface
  • Passing Parameters — Injected Parameters

Binding an interface

Kita akan mencontoh pada bagian Definition: binding an interface di dokumentasi Koin

// Service interface
interface Service{

fun doSomething()
}

// Service Implementation
class ServiceImp() : Service {

fun doSomething() { ... }
}

Kemudian kita lihat bagaimana cara membuat module & mendefiniskan nya

val myModule = module {

// Will match type Service only
single<Service> { ServiceImp() }

}

Nah kenapa kita melihat contoh binding an interface ? karena pada contoh MVP Pattern MainPresenter membutuhkan view interface dari MainView. Yuk kita bikin module nya

val myModule = module {

// declare MainView as single instance
single<MainView> { MainActivity() }
// declare MainPresenter as single instance
// resolving MainView instance with get()
single { MainPresenter(get()) }

}

Setelah itu yuk kita implementasikan pada MainActivity nya

class MainActivity : AppCompatActivity(),MainView {

private val TAG = this.javaClass.canonicalName

private val presenter: MainPresenter by inject()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
presenter.logMe("Android")
}

override fun logMessage(name: String) {
Log.e(TAG, name )
}
}

Yeay berhasilll !

Binding an Interface Implementation

Passing Parameters — Injected Parameters

Nah cara ini agak sedikit berbeda, kalau cara diatas kita mencoba membuat MainView dengan MainActivity sebagai implementasinya, dengan cara ini kita hanya memberikan nya saja, berikut contoh dari dokumentasi nya :

class Presenter(val view : View)

val myModule = module {
single { (view : View) -> Presenter(view) }
}

Okay setelah melihat contoh diatas yuk kita bikin module berdasarkan MVP Pattern kita :

val myModule = module {
factory { (view: MainView) ->
MainPresenter(view)
}
}

Setelah itu yuk kita implementasikan pada MainActivity nya

class MainActivity : AppCompatActivity(),MainView {

private val TAG = this.javaClass.canonicalName


private val presenter: MainPresenter by inject {
parametersOf(this)
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
presenter.logMe("PassingParameters")
}

override fun logMessage(name: String) {
Log.e(TAG, name )
}
}

Yeaay berhasil jugaaa

Passing Parameters — Injected Parameters

Jadi dengan menerapkan 2 cara di atas dapat kita petik kesimpulan bahwa

  • Binding an Interface : Mencoba membuat MainView dari Implementasi Class nya
  • Passing Parameters — Injected Parameters : Memberikan (Passing) MainView nya saja

Cukup mudah bukan ? Semoga membantu yaa, kalau mau melihat code nya bisa lihat di repo saya dibawah 😄

Terimakasih !
Sampai jumpa lagi

Sumber :

--

--

Ian Christian Adolfian Damping
Ian Christian Adolfian Damping

Written by Ian Christian Adolfian Damping

Always curious and always want to continue learning in many ways

No responses yet