Dependency Injection (DI, bog'liqliklarni injektsiya qilish) — ob'ekt o'z bog'liqliklarini o'zi yaratmasdan, tashqaridan oladigan texnika. DI IoC (Inversion of Control) printsipining amalga oshirilishi bo'lib, Dagger, Hilt va Swinject asosida yotadi. Bog'liqliklarni injektsiya qilish kodning bog'lanishini kamaytiradi, testlashni soddalashtiradi va arxitekturaga moslashuvchanlik beradi. Android-da DI standart sifatida Google-ning Dagger Hilt orqali, iOS-da esa Swinject yoki qo'lda injektsiya orqali amalga oshiriladi. Batafsil — Android DI Guide.
Asosiy fikrlar
Dependency Injection — ob'ekt bog'liqliklarni (servislar, repozitoriylar, konfiguratsiyalar) konstruktor, setter yoki interfeys orqali oladigan, o'zi new bilan yaratmaydigan texnika. DI ning maqsadi sinflar orasidagi bog'lanishni (coupling) kamaytirishdir. Agar sinf bog'liqliklarni o'zi yaratsa, u aniq implementatsiyalarga qattiq bog'lanadi, bu esa testlash va o'zgartirishni qiyinlashtiradi. DI da sinf abstraksiya (protocol/interface) bilan ishlaydi, aniq implementatsiya esa tashqaridan ta'minlanadi.
Uch xil injektsiya usuli — Constructor Injection (init/constructor orqali), Setter Injection (xususiyat/setter orqali), Interface Injection (interfeys metodi orqali). Constructor Injection — afzal ko'riladigan usul: bog'liqliklar imzoda aniq ko'rinadi, ob'ekt har doim to'g'ri holatda yaratiladi. Setter Injection ixtiyoriy bog'liqliklar uchun standart qiymat bilan ishlatiladi. Interface Injection — kamdan-kam, asosan DI konteynerlari uchun.
| DI turi | Usul | Qachon ishlatiladi | Misol |
|---|---|---|---|
| Constructor | Initsializator parametrlari | Majburiy bog'liqliklar | init(service: ServiceProtocol) |
| Property | Sinf xususiyati | Ixtiyoriy bog'liqliklar | var service: ServiceProtocol? |
| Method | Metod parametri | Vaqtinchalik bog'liqliklar | func doWork(with service: Service) |
DI konteyneri — bog'liqliklarni yaratish va hayot aylanishini boshqaradigan kutubxona. Konteyner turlarni ro'yxatga olishni (har bir abstrakt tur aniq implementatsiyaga moslashtiriladi) va hal qilingan bog'liqliklar bilan ob'ektlarni yaratish uchun fabrikani o'z ichiga oladi. Android-da — Dagger/Hilt, iOS-da — Swinject, Needle, Dip. Konteyner skopni (Scope) boshqarishi mumkin: singleton (ilova uchun bitta instansiya), funksiya skopi (ekran uchun) yoki har bir so'rovda yangi ob'ekt.
Dagger Hilt — Google Dagger ustidagi qatlam, Android uchun standart DI kutubxonasi. Hilt Dagger-ni soddalashtiradi: komponentlarni qo'lda yaratishni olib tashlaydi, @HiltAndroidApp, @AndroidEntryPoint va @Module qo'shadi. Hilt Android hayot aylanishi bilan integratsiyalanadi: ViewModel, Activity, Fragment, Service, BroadcastReceiver annotatsiyalar orqali bog'liqliklarni olishi mumkin. Kod generatsiyasi kompilyatsiya bosqichida sodir bo'ladi — Dagger komponentlarning implementatsiyalarini yaratadi, bu esa nol runtime overhead beradi.
// Application class
@HiltAndroidApp
class MyApp : Application()
// Module — bog'liqliklarni qanday yaratishni belgilaydi
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
@Provides
@Singleton
fun provideOkHttpClient(): OkHttpClient = OkHttpClient.Builder().build()
@Provides
@Singleton
fun provideApiService(client: OkHttpClient): ApiService {
return Retrofit.Builder()
.baseUrl("https://api.example.com")
.client(client)
.build()
.create(ApiService::class.java)
}
}
// ViewModel bog'liqlikni konstruktor orqali oladi
@HiltViewModel
class MainViewModel @Inject constructor(
private val apiService: ApiService
) : ViewModel() {
private val _state = MutableStateFlow(MainState.Loading)
val state: StateFlow<MainState> = _state.asStateFlow()
fun loadData() {
viewModelScope.launch {
_state.value = MainState.Success(apiService.getData())
}
}
}
// Activity — @AndroidEntryPoint DI ni faollashtiradi
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private val viewModel: MainViewModel by viewModels()
}
Dagger komponentlari va ko'rinish doiralari — @Singleton (butun ilova uchun), @ActivityScoped (Activity uchun), @FragmentScoped (Fragment uchun), @ViewModelScoped (ViewModel uchun). Skopni tanlash ob'ektning umrini belgilaydi. @Singleton — jarayon uchun bitta instansiya, OkHttpClient va ma'lumotlar bazasi uchun mos. @ActivityScoped — ob'ekt Activity yashaguncha yashaydi, ekran bog'liqliklari uchun. @ViewModelScoped — Hilt 2.45+ yangiligi, ob'ekt ViewModel yashaguncha yashaydi, korutin skoplari uchun qulay.
Swinject — iOS uchun ochiq manbali mashhur DI freymvorki. Swinject Container, Assemblies va turli skoplarni taqdim etadi. Dagger-dan farqli o'laroq, Swinject runtime-da ishlaydi — bog'liqliklar kod generatsiyasisiz dinamik hal qilinadi. Bu Swinject-ni sozlashda soddalashtiradi, lekin disk raskadrovkada qiyinchiliklar tug'diradi: hal qilinmagan bog'liqlik xatosi faqat runtime-da paydo bo'ladi. Swinject Constructor Injection, Property Injection va Method Injection-ni qo'llab-quvvatlaydi.
import Swinject
// Assembly — ro'yxatga olish guruhi
class NetworkAssembly: Assembly {
func assemble(container: Container) {
container.register(NetworkServiceProtocol.self) { _ in
NetworkService()
}.inObjectScope(.container) // singleton
container.register(UserRepositoryProtocol.self) { r in
UserRepository(
networkService: r.resolve(NetworkServiceProtocol.self)!
)
}
}
}
// ViewModel Constructor Injection orqali
class ProfileViewModel: ObservableObject {
private let repository: UserRepositoryProtocol
init(repository: UserRepositoryProtocol) {
self.repository = repository
}
@Published var user: User?
func loadUser() {
repository.fetchUser { [weak self] user in
self?.user = user
}
}
}
// AppDelegate yoki App da DI sozlamasi
let assembler = Assembler([NetworkAssembly(), ServiceAssembly()])
let viewModel = assembler.resolver.resolve(ProfileViewModel.self)!
// UIKit ViewController uchun Property Injection
container.register(UserViewController.self) { r in
let vc = UserViewController()
vc.viewModel = r.resolve(ProfileViewModel.self)
return vc
}
Swinject skoplari — .transient (har safar yangi ob'ekt), .container (konteyner uchun singleton), .graph (standart — ob'ekt bitta bog'liqlik grafi ichida taqsimlanadi). iOS ilovalari uchun .container va .transient yetarli. Swinject shuningdek Assembler-ni qo'llab-quvvatlaydi — modulli arxitektura uchun Assembly-larni guruhlash. Testlar uchun Assembly-lar MockAssembly bilan almashtiriladi, bu esa ishlab chiqarish kodini o'zgartirmasdan bog'liqliklarni almashtirish imkonini beradi.
DI vs Service Locator — ikkala pattern bog'liqliklarni boshqarish muammosini hal qiladi, lekin har xil usulda. DI bog'liqliklarni ob'ektga injektsiya qiladi, Service Locator global reestrni taqdim etadi, ob'ekt bog'liqliklarni o'zi undan oladi. DI bog'liqliklarni konstruktor (yoki setter) orqali aniq e'lon qiladi. Service Locator bog'liqliklarni yashiradi — ular metod ichida olinadi, bu esa imzoni kamroq informativ qiladi. DI ni testlash oson: konstruktorga mock berish kifoya. Service Locator har bir test uchun global reestrni sozlashni talab qiladi.
| Xususiyat | Dependency Injection | Service Locator | Qo'lda injektsiya |
|---|---|---|---|
| Bog'liqliklarning ochiqligi | Konstruktorda | Metod tanasida yashirin | Ochiq |
| Testlash | Konstruktorda Mock | Locator sozlamasi | Konstruktorda Mock |
| Sozlash murakkabligi | DI konteyner talab qiladi | Global reestr | Qo'lda yaratish |
| Runtime overhead | Dagger — compile-time | Runtime lookup | Yo'q |
DI vs qo'lda injektsiya — DI konteynerisiz bog'liqliklar fabrikalarda yoki AppDelegate-da qo'lda yaratiladi. 5-10 sinf uchun qo'lda injektsiya soddaroq — Dagger yoki Swinject o'rganish talab qilinmaydi. 50+ sinf uchun qo'lda injektsiya muammoga aylanadi: 5-6 parametrli konstruktorlar, murakkab yaratish tartibi, kod takrorlanishi. DI konteyneri bu jarayonlarni avtomatlashtiradi va aniq hayot aylanishini ta'minlaydi. Konteynersiz qo'lda injektsiya — kichik loyihalar va prototiplar uchun yaxshi tanlov.
Constructor Injection — standart. Majburiy bog'liqliklar uchun har doim Constructor Injection dan foydalaning. Bu bog'liqliklarni ochiq qiladi va ob'ektni har doim ishga tayyor holatda saqlaydi. Setter Injection — faqat ixtiyoriy bog'liqliklar uchun (masalan, delegate yoki listener). Interface Injection — o'z DI kutubxonangizni yozmasangiz, foydalanmang. Constructor Injection — ob'ektning to'g'ri holatda yaratilishini kafolatlaydigan yagona usul.
Bitta sinf — bitta mas'uliyat. Agar sinf konstruktori 5+ parametr talab qilsa, sinf Single Responsibility Principle ni buzayotgan bo'lishi mumkin. Sinfni kamroq bog'liqliklarga ega bir necha qismga bo'ling. Belgisi: 6 xil servisli ServiceManager sinfini yozayotgan bo'lsangiz — bu God Object anti-pattern. Biznes mantiqni Use Cases (Interactors) ga ajrating, har biri 1-2 bog'liqlik bilan.
// ❌ Yomon: 6 bog'liqlik — God Object
class ProfileViewModel @Inject constructor(
private val api: ApiService,
private val db: Database,
private val analytics: Analytics,
private val prefs: Preferences,
private val location: LocationProvider,
private val notification: NotificationManager
)
// ✅ Yaxshi: 1-2 bog'liqlik bilan Use Cases
class ProfileViewModel @Inject constructor(
private val loadProfileUseCase: LoadProfileUseCase,
private val trackAnalyticsUseCase: TrackAnalyticsUseCase
)
Skop va hayot aylanishi — har bir bog'liqlik uchun to'g'ri skopni tanlang. Singleton: OkHttpClient, ma'lumotlar bazasi, SharedPreferences. Funksiya skopi: repozitoriylar, Use Cases (agar holati bo'lmasa). Transient: Value Objects, DateFormatter, parserlar. Skop xatosi — tez-tez uchraydigan muammo: ekran holatini saqlaydigan singleton xotira oqishiga olib keladi. Android Hilt da @ActivityScoped bu muammoni hal qiladi, Swinject da — .container ehtiyot bilan.
Tez-tez beriladigan savollar
new sinflar orasida qattiq bog'lanish yaratadi — kodni o'zgartirmasdan implementatsiyani almashtira olmaysiz. Testlash qiyin: haqiqiy servis o'rniga mock qo'ya olmaysiz. SRP buziladi: sinf ham biznes mantiq, ham bog'liqliklarni yaratish uchun javobgar bo'ladi. DI bu muammolarni bog'liqliklarni tashqaridan injektsiya qilish va abstraksiyalar bilan ishlash orqali hal qiladi.
Dagger Hilt — Google standarti, kod generatsiyasi bilan compile-time DI, yaxshiroq unumdorlik va Jetpack bilan integratsiya. Koin — runtime DI, sozlashda soddaroq, lekin sekinroq va runtime xatolari bilan. Ishlab chiqarish loyihalari uchun Hilt-ni tanlang. Koin prototiplar va kichik ilovalar uchun mos.
Yo'q. iOS uchun mavjud: Swinject (runtime, mashhur), Needle (Uber-dan compile-time), Dip (yengil), Weaver (Sourcery asosida). Apple o'rnatilgan DI konteynerini taqdim etmaydi, lekin init orqali qo'lda injektsiya standart amaliyotdir. SwiftUI uchun ko'pincha tashqi kutubxonalarsiz Environment yoki @StateObject orqali qo'lda DI yetarli.
Ha. Konstruktor orqali qo'lda injektsiya — bu freymvorksiz DI. Service Locator — freymvorksiz alternativ. Fabrikalar va Factory Method — ham DI shakli. Freymvork (Dagger, Swinject) muntazam ro'yxatga olish va bog'liqliklarni hal qilishni avtomatlashtiradi, lekin 10-20 sinf uchun qo'lda DI yetarli.
DI — Inversion of Control printsipini amalga oshiradigan texnika (pattern). GoF patternlaridan farqli o'laroq, DI 3-4 sinfdan iborat qat'iy tuzilmaga ega emas. DI bog'liqliklarni tashkil qilish usuli, dizayn patterni emas. DI konteynerlari (Dagger, Swinject) — bu texnikani avtomatlashtiradigan freymvorklardir.
Xulosa
Biz kalit topshirig'i bilan mobil ilovani ishlab chiqamiz
IT Sectr 2017-yildan beri startaplar va korxonalar uchun iOS va Android ilovalarini yaratadi. Biz sizga maslahat beramiz va eng yaxshi yechimni taklif qilamiz.