koin چیست؟ تزریق وابستگی با استفاده از فریم ورک Koin در کاتلین

دسته بندی: برنامه نویسی
زمان مطالعه: 9 دقیقه
۳۱ فروردین ۱۳۹۹

koin چیست؟

طبق اصول S.O.L.I.D یک کلاس باید فقط متمرکز به انجام وظایف خود باشد و نباید درگیر نمونه‌گیری از سایر کلاس‌ها برای انجام مسئولیت‌های خود باشد.

در یک پروژه‌ی ساده، نمونه‌گیری از کلاس‌ها کاری است که می‌توان به صورت دستی انجام داد، اما در برخی موارد لازم است که این نمونه‌گیری به صورت خودکار با استفاده از مثلاً یک فریمورک تزریق وابستگی انجام شود. برای درک بهتر این مسئله بهتر است ابتدا مفهوم وابستگی را در برنامه‌نویسی مرور کنیم و سپس به حل مسئله بپردازیم.

فهرست محتوای این مقاله

مفهوم وابستگی در برنامه نویسی

برای درک بهتر مفهوم وابستگی توضیح آن را با یک مثال ادامه می‌دهیم. فرض کنید در پروژه‌ی خود دو کلاس با نام‌های ClassA و ClassB داریم که به‌صورت زیر تعریف شده‌اند:

class ClassA {
var classB: ClassB

}

class ClassB {}

همان‌طور که می‌بینید، در ClassA از ClassB استفاده شده است. بنابراین می‌توانیم بگوییم که ClassA به ClassB وابسته است. یا به عبارتی کلاس B یک وابستگی (dependency) برای کلاس A محسوب می‌شود چون ClassA برای انجام دادن وظیفه‌اش باید از ClassB استفاده کند.

از طرفی هر وقت به کلاس A نیاز داشته باشیم حتماً باید از کلاس B هم استفاده کنیم. بنابراین جایگزین کردن ClassB  با یک کلاس دیگر عملاً غیرممکن است. و از طرفی با هر تغییری در ورودی‌های سازنده‌ی کلاس B نیازمند اعمال تغییرات در کلاس A نیز هستیم. مثلاً اگر به سازنده‌ی کلاس B پارامتری اضافه کنیم، باید به تمام کلاس‌های وابسته از جمله کلاس A برویم و نمونه‌های ساخته شده از کلاس B را بروز‌‌رسانی کنیم. که این امر خود، نقض قاعده‌ی Single Responsibility یا تک مسئولیتی کلاس‌ها که یکی از مهم‌ترین اصول S.O.L.I.D  به شما می‌رود، نیز است.

تزریق وابستگی چیست؟

تزریق وابستگی روشی برای مدیریت وابستگی‌ها در جایی خارج از کلاس‌های وابسته است طوری که دیگر کلاس وابسته نگران مدیریت وابستگی‌های خود نخواهد بود.

Dependency injection یا همان تزریق وابستگی که به اختصار به آن DI نیز گفته می‌شود، یک نوع ایده یا روش پیاده‌سازی کدهای پروژه است و یک تکنولوژی، فریم ورک، کتابخانه یا هر چیز دیگری شبیه این‌ها نیست.

تزریق وابستگی یک تکنولوژی است که با الهام گرفتن از اصل پنجم S.O.L.I.D ، یک کلاس را نسبت به وابستگی‌های داخل آن مستقل می‌کند.

مزایای استفاده از تزریق وابستگی

وابستگی بین اجزای مختلف پروژه سست می‌شود و سبب انسجام بیشتر کدهای پروژه می‌شود.

امکان تست هر یک از بخش‌های پروژه را به صورت جداگانه فراهم می‌کند.

تغییر در ورودی‌های سازنده‌ی کلاس‌ها و به طور کلی ایجاد تغییر و یا گسترش اجزا را بسیار ساده‌تر می‌کند.

فریم ورک koin چیست؟

تزریق وابستگی را می‌توان بدون کتابخانه یا فریم‌ورک خاصی هم انجام داد اما چندین فریم‌ورک‌ برای پیاده‌سازی آن موجود است که برای ساده کردن این فرآیند عرضه شده‌اند.

Koin یکی از این فریم ورک‌هاست که برای برنامه نویسی به زبان کاتلین تعریف شده است و یک فریم ورک بسیار قوی اما در حین حال ساده و کم حجم است و به برنامه نویسان کمک می‌کند به ساده‌ترین شکل ممکن تزریق وابستگی را در پروژه‌ی کاتلین خود پیاده‌سازی کنند.

مراحل پیاده‌سازی koin چیست؟

برای پیاده‌سازی پروژه با  کوین در یک پروژه اندرویدی باید پنج گام را به صورت زیر طی کرد:

بیشتر بدانیم: برنامه نویسی اندروید چیست؟

گام 1: افزودن وابستگی‌های مورد نیاز در فایل Gradle

در ابتدا جهت استفاده از فریم ورک کوین باید وابستگی‌های آن را  در پروژه خود قرار دهید. شما باید کد زیر را به "build.gradle" در سطح ماژول خود اضافه کنید:

dependencies {

def koin_version= "2.1.5"

   // Koin AndroidX Scope feature
   implementation "org.koin:koin-androidx-scope:$koin_version" 

   // Koin AndroidX ViewModel feature
   implementation "org.koin:koin-androidx-viewmodel:$koin_version"
  
  // Koin AndroidX Fragment Factory (unstable version)
  implementation "org.koin:koin-androidx-fragment:$koin_version"

}

گام 2: تعریف Data class  و Repository

در گام دومِ مراحل پیاده‌سازی koin چیست؟ ابتدا یک کلاس دیتا ایجاد می‌کنیم که آیدی ، نام و نام کاربری یک کاربر را برای ما نگه دارد. برای انجام این‌کار کلاس دیتایی با نام SevenLearnUser به صورت زیر ایجاد می‌کنیم:

data class SevenLearnUser(val id: Int?, val name: String?, val username: String)

پس از ایجاد کلاس دیتا حال نوبت به کلاس مخزن می‌رسد. در این مرحله از کار قصد داریم کلاسی را بسازیم که لیست کاربران را برای ما برگرداند. این اطلاعات به طور معمول از دیتابیس محلی و یا دیتابیس سرور توسط وب سرویس‌ها دریافت می‌شود اما در این مثال ما به ایجاد اطلاعات تستی به صورت زیر بسنده می‌کنیم:


interface UserRepository {
    fun fetchUsers(): List<SevenLearnUser>
}

class UserRepositoryImpl : UserRepository {
    override fun fetchUsers(): List<SevenLearnUser> {
        return listOf(
                SevenLearnUser(1, name = "Alireza", username = "@Alireza1010"),
                SevenLearnUser(2, name = "Mohammad", username = "@Mohammad01"),
                SevenLearnUser(3, name = "Zeynab", username = "@Zeynab1210"),
                SevenLearnUser(4, name = "Vahid ", username = "@paradise"),
                SevenLearnUser(5, name = "Elham", username = "@Ely2000")
        )
    }
}


class SevenLearnUserPresenter(val repo: UserRepository) {
    fun getSevenLearnUsers(): List<SevenLearnUser> = repo.fetchUsers()
}

 

در ادامه با فراخوانی کلاس SevenLearnUserPresenter می‌توانیم لیست کاربران ایجاد شده را دریافت نماییم.

گام 3: تعریف ماژول‌ها جهت فراهم کردن وابستگی‌ها

در سومین گام باید به ساخت ماژول‌های کوین در پروژه خود بپردازیم. هر ماژول در پروژه به عنوان یک ظرف برای مجموعه‌ای از سرویس‌هایی است که قرار است در زمان اجرا فراخوانی شوند. ما تمام خدماتی را که یک ماژول باید ارائه دهد را در بلوک ماژول قرار می‌دهیم.

        val appModule = module {
            
        // Single instance of UserRepository - Singleton
            single<UserRepository> { UserRepositoryImpl() }

        // Simple Presenter Factory - new instances created each time
            factory { SevenLearnUserPresenter(get()) }
        }

در هر ماژول باید یک تا تعدادی Factory و Single تعریف شود که با هدف کنترل و توصیف وابستگی‌ها تعریف می‌شوند.

تفاوت Factory و Single در این است که زمانی که یک کلاس در ماژول به عنوان Single تعریف می‌شود، کوین یک بار از آن نمونه‌گیری می‌کند و آن نمونه را نگه می‌دارد. و در هر کجا از پروژه که نیاز باشد آن نمونه را برمی‌گرداند، اما زمانی که کلاسی به عنوان Factory تعریف شود کوین هر زمان که به آن نیاز باشد یک نمونه جدید از آن را می‌سازد.

گام 4: تعریف کوین در پروژه

پس از ایجاد ماژول‌ها حال نوبت به تعریف کوین در سطح پروژه و فراخوانی ماژول‌ها در آن می‌رسد. برای انجام این کار ابتدا کلاسی با نام MyApp ایجاد می‌کنیم که از کلاس Application ارث می‌برد. و در آن به صورت زیر تابعی به نام startKoin را فراخوانی می‌کنیم.

class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()

        // Start Koin
        startKoin {
            androidLogger()
            androidContext(this@MyApp)
            modules(appModule)
        }

    }
    }

در این بخش با فراخوانی تابع startKoin در واقع وابستگی‌هایی را که در ماژول‌ها تعریف کرده‌ایم را بارگذاری می‌کند. در این بخش اگر تعداد ماژول‌های بیشتری در پروژه‌ی خود دارید. باید همه آنها را در startKoin وارد نمایید.

در ادامه نیز کلاس MyApp را باید در فایل مانیفست پروژه به صورت زیر تعریف کنیم:

<application
android:name=".MyApp"
.
.
.

گام 5: تزریق وابستگی‌ها

در آخرین گام نیز می‌خواهیم در یک اکتیویتی لیست کاربران را نمایش بدهیم ، برای این‌کار نیاز به کلاس SevenLearnUserPresenter داریم تا لیست کاربران را برای ما برگرداند و از آن برای نمایش در طرح استفاده کنیم. بنابراین نیاز داریم کلاس SevenLearnUserPresenter را که در مرحله‌ی سوم به عنوان factory تعریف کرده بودیم را به MainActivity خود تزریق کنیم.

دو روش برای فراخوانی این کلاس وجود دارد، استفاده از inject (تزریق) و یا استفاده از تابع ()get. تابع inject به ما این امکان را می‌دهد که نمونه گیری را در زمان اجرا (runtime) انجام دهیم و تابع get مستقیما نمونه‌ای از کلاس مورد نظر را برای ما برمی‌گرداند.

class MainActivity : AppCompatActivity() {

    // Lazy injected GithubUserPresenter
    private val presenter: SevenLearnUserPresenter by inject()


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

        for (student in presenter.getSevenLearnUsers()) {
            Log.d("",student.toString())
        }
    }
  }

 

در این بخش از کار متغیری با نام presenter از جنس SevenLearnUserPresenter ایجاد کرده‌ایم و حال به جای نمونه گیری مستقیم از کلاس SevenLearnUserPresenter آن را به صورت inject فراخوانی کرده‌ایم.

private val presenter: SevenLearnUserPresenter by inject()

به این نکته دقت کنید که تابع SevenLearnUserPresenter  برای فراخوانی نیاز به کلاس UserRepository دارد، در حالت عادی و بدون استفاده از تزریق وابستگی، هر کجا که از آن نمونه‌ای گرفته می‌شود باید یک نمونه از UserRepository به ورودی آن داده شود. اما دیدیم که با استفاده از تزریق وابستگی برای نمونه گیری از SevenLearnUserPresenter در اکتیویتی نیازی به تعریف و فراخوانی UserRepository نبود. بنابراین اگر نیاز به تغییر در ورودی‌های کلاس SevenLearnUserPresenter داشتیم نیازی نیست در تمام کلاس‌هایی که از آن استفاده می‌کنند(در این مثال MainActivity)، آن تغییرات را اعمال کنیم.

مزیت دوم این روش نیز این است که به راحتی می‌توان UserRepository را در این کلاس جایگزین کلاس دیگری کرد و همچنین امکان تست گرفتن مجزای هر کلاس برای ما فراهم می‌شود.

جمع بندی:

بزرگترین کار یک مهندس نرم افزار توسعه نیست بلکه بقا و نگهداری و گسترش پروژه است، هرچه بیشتر کدهایتان بر اساس یک معماری خوب تهیه شود، تست بخش‌های مختلف آن ساده‌تر خواهد بود و در مجموع نگهداری از آن کار ساده‌تری خواهد بود، به همین دلیل استفاده از الگوهای مناسب مانند تزریق وابستگی در یک پروژه اهمیت بالایی دارد. آیا شما تجربه‌ی استفاده از این فریم ورک را داشته اید، در صورت استفاده نظرات خود را با کاربران وب سایت سون لرن به اشتراک بگذارید.

اگر به یادگیری بیشتر در زمینه‌ی اندروید علاقه داری، با شرکت در دوره‌ی آموزشی متخصص اندروید در کمتر از یکسال به یک توسعه‌دهنده اندروید همه فن حریف تبدیل می‌شوی که آماده‌ی استخدام، دریافت پروژه و حتی پیاده‌سازی اپلیکیشن خودت هستی.

 

چه امتیازی به این مقاله می دید؟
نویسنده علیرضا اسلمی

نظرات کاربران

اولین دیدگاه این پست رو تو بنویس !

ارسال دیدگاه
خوشحال میشیم دیدگاه و یا تجربیات خودتون رو با ما در میون بذارید :