خانه » Dependency injection » Koin را وارد کنید (قسمت اول)

Koin را وارد کنید (قسمت اول)

Koin چیست ؟! (این آموزش مربوط به زبان Kotlin می باشد)

برای اینکه بدونید Koin چی هست ابتدا باید بدنید Dependency injection چی هست ؟!!! ( اگر میدونید Dependency injection چی هست به بخش دوم مقاله مراجعه کنید)

به نظر یکم ترسناک میاد اما واقعا اونقدری که به نظر میاد ترسناک نیست

 

Dependency injection یا DI یا تزریق وابستگی :

برای توضیح این مبحث به مثالی که در ادامه آوردم توجه کنید.

در این مثال تنها ۲ کلاس داریم، یکی MainActivity یکی هم DataProvider که کارش اینه که دیتای مارو تامین کنه.

قراره یک دکمه وسط صفحه بزاریم و وقتی که روی این دکمه کلیک شد از DataProvider مقدار userName رو بخونیم و بعد اون رو روی صفحه نشون بدیم.

پس ابتدا این ۲ کلاس رو میسازیم:

DataProvider:

package ir.iact.koinSample

class DataProvider {
    fun getUserName(): String {
        return "Eiliya"
    }
}

کار این کلاس این هست که زمانی که متد getUserName صدا زده شد مقدار Eiliya رو برای ما برگرودنه

MainActivity:

package ir.iact.koinSample

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    private val dataProvider: DataProvider = DataProvider()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        getUsernameButton.setOnClickListener {
            val userName = dataProvider.getUserName()
            Toast.makeText(this, "Username is: $userName", Toast.LENGTH_LONG).show()
        }

    }
}

و کلاس MainActivity کارش این هست که برای دکمه listener تعریف کنه (در این پروژه از synthetic به جای findViewById استفاده شده) که زمانی که روی اون کلیک شد از dataProvider متد getUserName رو صدا بزنه و مقدار بازگشتی رو با Toast نشون بده.

(commit name: Step 1: Simple DataProvider )

خوب اگر به کد بالا نگاه کنید ما کلاس DataProvider رو در سطح کلاس new کردیم و یک instance از اون رو ساختیم، و سپس از اون زمان کلیک شدن استفاده کردیم.

خوب همه چیز تا اینجا خوبه، حالا فکر کنید که ۲ تا Activity ما بخوان از این کلاس استفاده کنن یا حتی یک سرویس همزمان قصد استفاده از این کلاس رو داشته باشه ، در اون صورت هر کدوم برای خودشون یک instance جدید باید بسازن که زیاد کار جالبی به نظر نمیاد، واسه اینکه از این کار جلوگیری کنیم ما از یک design pattern به اسم singleton استفاده می کنیم که حتما خیلی وقته که اون رو میشناسید و ازش استفاده کردید ، خوب پس کد رو کمی تغییر میدیم تا به شکل زیر بشه:

کلاس DataProvider:

package ir.iact.koinSample

class DataProvider {
    fun getUserName(): String {
        return "Eiliya"
    }

    companion object {
        val instance: DataProvider = DataProvider()
    }
}

کلاس MainActivity:

package ir.iact.koinSample

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        getUsernameButton.setOnClickListener {
            val userName = DataProvider.instance.getUserName()
            Toast.makeText(this, "Username is: $userName", Toast.LENGTH_LONG).show()
        }

    }
}

خوب الان فقط یکبار این کلاس ساخته میشه و مشکل ما ظاهرا حل شد

(commit name: Step 2: Make it singleton )

حالا فکر کنید در روند پروژه زمانی پیش میاد که قصد تعویض DataProvide با یک کلاس دیگر رو دارید و چاره ای ندارید جز اینکه تمام بخش های کد رو دوباره تغییر بدید و از کلاس جدید استفاده کنید و از اون مهم تر زمانی که قصد نوشتن تست رو دارید، قصد دارید به جای کلاس DataProvider استفاده شده ، کلاس دیگری رو برای Mock کردن دیتا استفاده کنید و نحوه کار کردن MainActivity رو تست کنید، خوب برای حل این مشکل معمولا یک کلاس واسط نوشته میشه به  مثال زیر توجه کنید:

 

AppModule:

package ir.iact.koinSample

class AppModule {
    companion object {
        fun getDataProvider(): DataProvider {
            return DataProvider.instance
        }

    }
}

MainActivity:

package ir.iact.koinSample

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    private val dataProvider: DataProvider = AppModule.getDataProvider()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        getUsernameButton.setOnClickListener {
            val userName = dataProvider.getUserName()
            Toast.makeText(this, "Username is: $userName", Toast.LENGTH_LONG).show()
        }

    }
}

 

این دفعه کلاس MainActivity به جای اینکه مستقیم از DataProvider استفاده کنه از کلاس واسطی به اسم AppModule یه instance از اون کلاس رو درخواست می کنه و AppModule وظیفه تامین این instance و یا به عبارتی این وابستگی رو داره.

(commit name: Step 3: Add simple module )

 

دیدید ما یک تزریق وابستگی انجام دادیم، حالا زمانی که قصد تست برنامه رو داریم کافیه که مقادیر AppModule رو تغییر بدیم و نحوه ساخته شدن instance رو تغییر بدیم.

 

تا اینجا به صورت خیلی ساده مفاهیم اولیه تزریق وابستگی رو یاد گرفتیم، حالا قصد داریم که از Koin استفاده کنیم تا به جای کلاس AppModule این لایبرری این کار رو برای ما انجام بده.

 

استفاده از Koin

۱ – برای استفاده از این لایبرری ابتدا باید اون رو به dependencies ها اضافه و سپس پروژه رو sync کنید:

  // Koin library
    implementation 'org.koin:koin-android:1.0.2'

۲ – سپس باید ماژول رو تعریف کرد، این کار با کد زیر انجام میشه:

val appModule = module {
    
}

درواقع ما یک از لایبرری Koin رو صدا می زنیم تا برای ما این ماژول رو بسازه

 

۳ – حالا باید نحوه ساخته شدن instance رو مشخص کنیم:

val appModule = module {
    single {
        DataProvider()
    }
}

چون ما قصد داریم که کلاس ما به صورت singleton ساخته شود این کار رو با متد single انجام میدیم ( با این کار دیگه نیاز نیست داخل کلاس از companion object برای ساخت singleton استفاده کنیم ، پس کلاس DataProvider ما به شکل زیر می شود:

class DataProvider {
    fun getUserName(): String {
        return "Eiliya"
    }
}

 

)

۴ – بعد از این کار باید ماژول هارو به لایبرری Koin معرفی کنیم :

        startKoin(applicationContext, listOf(appModule))

بهتر هست که این کد در کلاس Application نوشته شود اما برای ساده تر شدن مثال این رو داخل MainActivity نوشتم.

خوب با متد startKoin به Koin ماژول هارو معرفی می کنیم، ورودی اول Context هست و ورودی دوم لیسی از ماژول ها که در اینجا appModule رو که در قبل تر ساخته بودیم به عنوان ورودی میدیم.

 

۵ – خوب تا اینجا ۹۰ درصد کار انجام شده فقط موند خط آخر، داخل کلاس MainActivity به صورت زیرinstance از DataProvider رو می گیریم:

    private val dataProvider: DataProvider by inject()

در این خط inject یکی از متد های کلاس Koin هست که کار تامین وابستگی رو انجام میده اون هم به صورت lazy یعنی فقط وقتی این instance مقدار میگیره که برای اولین بار قصد استفاده از اون رو داشته باشیم.

تمام شد حالا زمانی که ما از inject() در هر جایی از برنامه استفاده می کنیم ، لایبرری Koin برای ما سیم کشی می کند و با استفاده از دیتای appModule این وابستگی رو تامین می کند.

در نهایت این ۳ کلاس به شکل زیر می شوند:

کلاس DataProvider:

package ir.iact.koinSample

class DataProvider {
    fun getUserName(): String {
        return "Eiliya"
    }
}

کلاس MainActivity:

package ir.iact.koinSample

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_main.*
import org.koin.android.ext.android.inject
import org.koin.android.ext.android.startKoin

class MainActivity : AppCompatActivity() {

    private val dataProvider: DataProvider by inject()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        startKoin(applicationContext, listOf(appModule))

        getUsernameButton.setOnClickListener {
            val userName = dataProvider.getUserName()
            Toast.makeText(this, "Username is: $userName", Toast.LENGTH_LONG).show()
        }


    }
}

کلاس AppModule:

package ir.iact.koinSample

import org.koin.dsl.module.module

val appModule = module {
    single {
        DataProvider()
    }
}

(commit name: Step 4: Insert koin )

خوب تا اینجا به صورت خیلی ساده و با کاربرد خیلی ساده از لایبرری Koin استفاده کردیم در قسمت بعدی آموزش کمی مطلب رو گسترش میدیم

صفحه مربوط به لایبرری:

https://insert-koin.io/

صفحه مربوط به داکومنت خود لایبرری:

https://insert-koin.io/docs/1.0/getting-started/android/

 

و در نهایت کدهای این پروژه رو میتونید در این لینک گیت هاب ببینید:

https://github.com/eiliyaabedini/koin-sample

 

قسمت دوم آموزش

برچسب ها:
پست قبلی
پست بعدی

درباره ایلیا عابدینی

برنامه نویس اندروید و کارشناس مهندسی پزشکی، نفر سوم مسابقه برنامه نویسی اندروید http://www.schallenge.ir ، از سال 92 برنامه نویسی اندروید رو شروع کردم و در حال حاضر در شرکت ارتباط کنترل فراگستر در حال توسعه اپلیکیشن های اندرویدی می باشدم ، این وبلاگ رو ساختم تا تجربیات روزانه و مفید خودم رو داخل اون بزارم. رزومه : iact.ir/cv

دیدگاهتان را ثبت کنید

آدرس ایمیل شما منتشر نمی شود.علامت دارها لازمند. *

*

رفتن به بالا