Dependency Injection: bu nədir, iOS və Android-də asılılıqların inject edilməsi

Müəllif: IT Sectr Dərc olunub: 2026-02-18 Oxuma vaxtı: 9 dəq

Dependency Injection (DI, asılılıqların inject edilməsi) — obyektin asılılıqlarını özü yaratmayıb xaricdən aldığı texnikadır. DI IoC (Inversion of Control) prinsipinin tətbiqidir və Dagger, Hilt və Swinject-in əsasında durur. Asılılıqların inject edilməsi kodun bağlılığını azaldır, testləri sadələşdirir və arxitekturanı çevik edir. Android-də DI standart olaraq Google-un Dagger Hilt-i ilə, iOS-da isə Swinject və ya əl ilə inject etmə ilə həyata keçirilir. Ətraflı — Android DI Guide.

Əsas məqamlar

  • Dependency Injection — asılılıqlar obyektə xaricdən ötürülür, daxildə yaradılmır
  • Inversion of Control — DI IoC prinsipini tətbiq edir, idarəetmə axını konteynerə verilir
  • Dagger Hilt — Android üçün DI standartı, Google-un Dagger-i əsasında
  • Swinject — iOS və Swift üçün məşhur DI freymvorku
  • Bağlılığın azaldılması — klass abstraksiyalardan asılıdır, konkret tətbiqlərdən yox

Dependency Injection nədir: mahiyyəti və DI növləri

Dependency Injection — obyektin asılılıqları (servislər, repozitoriyalar, konfiqurasiyalar) konstruktor, setter və ya interfeys vasitəsilə aldığı, özü new ilə yaratmadığı texnikadır. DI-nin məqsədi siniflər arasında bağlılığı (coupling) azaltmaqdır. Əgər sinif asılılıqları özü yaradırsa, o, konkret tətbiqlərə sərt bağlanır ki, bu da test etməni və dəyişikliyi çətinləşdirir. DI-də sinif abstraksiya (protocol/interface) ilə işləyir, konkret tətbiq isə xaricdən verilir.

Üç inject etmə üsulu — Constructor Injection (init/constructor vasitəsilə), Setter Injection (xüsusiyyət/setter vasitəsilə), Interface Injection (interfeys metodu vasitəsilə). Constructor Injection — üstünlük verilən üsul: asılılıqlar imzada aydın görünür, obyekt həmişə etibarlı vəziyyətdə yaradılır. Setter Injection isteğe bağlı asılılıqlar üçün default dəyərlə istifadə olunur. Interface Injection — nadir, əsasən DI konteynerləri üçün.

DI növüÜsulNə vaxt istifadə olunurNümunə
Constructorİnisializator parametrləriMəcburi asılılıqlarinit(service: ServiceProtocol)
PropertySinif xüsusiyyətiİsteğe bağlı asılılıqlarvar service: ServiceProtocol?
MethodMetod parametriMüvəqqəti asılılıqlarfunc doWork(with service: Service)

DI konteyneri — asılılıqların yaradılmasını və həyat dövrünü idarə edən kitabxana. Konteyner tiplərin qeydiyyatını (hər abstrakt tip konkret tətbiqlə əlaqələndirilir) və həll edilmiş asılılıqlarla obyektlərin yaradılması üçün fabriki ehtiva edir. Android-də — Dagger/Hilt, iOS-da — Swinject, Needle, Dip. Konteyner skopu (Scope) idarə edə bilər: singleton (tətbiq üçün bir instansiya), funksiya skopu (ekran üçün) və ya hər sorğuda yeni obyekt.

Dagger Hilt: Android üçün kodgenerasiya ilə DI

Dagger Hilt — Google-un Dagger üzərində qurulmuş qatı, Android üçün standart DI kitabxanası. Hilt Dagger-i sadələşdirir: komponentlərin əl ilə yaradılmasını aradan qaldırır, @HiltAndroidApp, @AndroidEntryPoint və @Module əlavə edir. Hilt Android həyat dövrü ilə inteqrasiya olunur: ViewModel, Activity, Fragment, Service, BroadcastReceiver annotasiyalar vasitəsilə asılılıqları ala bilər. Kodgenerasiya kompilyasiya mərhələsində baş verir — Dagger komponentlərin implementasiyalarını yaradır ki, bu da sıfır runtime overhead verir.

kotlin
// Application class
@HiltAndroidApp
class MyApp : Application()

// Module — asılılıqların necə yaradılacağını müəyyən edir
@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 asılılığı konstruktor vasitəsilə alır
@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 aktivləşdirir
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    private val viewModel: MainViewModel by viewModels()
}

Dagger komponentləri və görünmə sahələri — @Singleton (bütün tətbiq üçün), @ActivityScoped (Activity üçün), @FragmentScoped (Fragment üçün), @ViewModelScoped (ViewModel üçün). Skop seçimi obyektin ömrünü müəyyən edir. @Singleton — proses üçün bir instansiya, OkHttpClient və verilənlər bazası üçün uyğundur. @ActivityScoped — obyekt Activity yaşadıqca yaşayır, ekran asılılıqları üçün. @ViewModelScoped — Hilt 2.45+ yeniliyi, obyekt ViewModel yaşadıqca yaşayır, korutin skopları üçün əlverişlidir.

Swinject: iOS üçün Swift-də DI

Swinject — iOS üçün açıq mənbəli məşhur DI freymvorku. Swinject Container, Assemblies və müxtəlif skoplar təqdim edir. Dagger-dən fərqli olaraq, Swinject runtime-da işləyir — asılılıqlar kodgenerasiya olmadan dinamik həll edilir. Bu, Swinject-i konfiqurasiyada sadələşdirir, lakin debug-da çətinlik yaradır: həll edilməmiş asılılıq xətası yalnız runtime-da üzə çıxır. Swinject Constructor Injection, Property Injection və Method Injection dəstəkləyir.

swift
import Swinject

// Assembly — qeydiyyat qrupu
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 vasitəsilə
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 və ya App-də DI konfiqurasiyası
let assembler = Assembler([NetworkAssembly(), ServiceAssembly()])
let viewModel = assembler.resolver.resolve(ProfileViewModel.self)!

// UIKit ViewController üçün Property Injection
container.register(UserViewController.self) { r in
    let vc = UserViewController()
    vc.viewModel = r.resolve(ProfileViewModel.self)
    return vc
}

Swinject skopları — .transient (hər dəfə yeni obyekt), .container (konteyner üçün singleton), .graph (defolt — obyekt bir asılılıq qrafiki daxilində paylaşılır). iOS tətbiqləri üçün .container və .transient kifayətdir. Swinject həmçinin Assembler-i dəstəkləyir — modul arxitekturası üçün Assembly-lərin qruplaşdırılması. Testlər üçün Assembly-lər MockAssembly ilə əvəz olunur ki, bu da istehsalat kodunu dəyişmədən asılılıqları əvəz etməyə imkan verir.

DI-nin Service Locator və əl ilə inject etmə ilə müqayisəsi

DI vs Service Locator — hər iki naxış asılılıqların idarə edilməsi problemini həll edir, lakin fərqli şəkildə. DI asılılıqları obyektə inject edir, Service Locator isə qlobal reyestr təqdim edir, obyekt asılılıqları özü oradan götürür. DI asılılıqları konstruktor (və ya setter) vasitəsilə açıq şəkildə bəyan edir. Service Locator asılılıqları gizlədir — onlar metodun daxilində götürülür ki, bu da imzanı daha az informativ edir. DI-ni test etmək asandır: konstruktora mock vermək kifayətdir. Service Locator hər test üçün qlobal reyestrin konfiqurasiyasını tələb edir.

XüsusiyyətDependency InjectionService LocatorƏl ilə inject etmə
Asılılıqların aşkarlığıKonstruktordaMetod gövdəsində gizliAşkar
Test etməKonstruktorda MockLocator-un konfiqurasiyasıKonstruktorda Mock
Konfiqurasiya mürəkkəbliyiDI konteyneri tələb edirQlobal reyestrƏl ilə yaratma
Runtime overheadDagger — compile-timeRuntime lookupYoxdur

DI vs əl ilə inject etmə — DI konteyneri olmadan asılılıqlar fabriklərdə və ya AppDelegate-də əl ilə yaradılır. 5-10 sinif üçün əl ilə inject etmə daha sadədir — Dagger və ya Swinject öyrənmək tələb olunmur. 50+ sinif üçün əl ilə inject etmə problemə çevrilir: 5-6 parametrli konstruktorlar, mürəkkəb yaratma ardıcıllığı, kodun təkrarlanması. DI konteyneri bu prosesləri avtomatlaşdırır və aydın lifecycle təmin edir. Konteynersiz əl ilə inject etmə — kiçik layihələr və prototiplər üçün yaxşı seçimdir.

Dependency Injection-ın ən yaxşı təcrübələri

Constructor Injection — standart. Məcburi asılılıqlar üçün həmişə Constructor Injection istifadə edin. Bu, asılılıqları aşkar edir və obyekti həmişə işə hazır vəziyyətdə saxlayır. Setter Injection — yalnız isteğe bağlı asılılıqlar üçün (məsələn, delegate və ya listener). Interface Injection — öz DI kitabxananızı yazmırsınızsa, istifadə etməyin. Constructor Injection — obyektin etibarlı vəziyyətdə yaradılmasını təmin edən yeganə üsuldur.

Bir sinif — bir məsuliyyət. Sinif konstruktoru 5+ parametr tələb edirsə, çox güman ki, sinif Single Responsibility Principle-i pozur. Sinifi daha az asılılığı olan bir neçə hissəyə bölün. Əlamət: 6 fərqli servisli ServiceManager sinfi yazırsınızsa — bu God Object anti-naxışıdır. Biznes məntiqini Use Cases (Interactors) ilə ayırın, hər biri 1-2 asılılıqla.

kotlin
// ❌ Pis: 6 asılılıq — 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
)

// ✅ Yaxşı: 1-2 asılılığı olan Use Cases
class ProfileViewModel @Inject constructor(
    private val loadProfileUseCase: LoadProfileUseCase,
    private val trackAnalyticsUseCase: TrackAnalyticsUseCase
)

Skop və lifecycle — hər asılılıq üçün düzgün skop seçin. Singleton: OkHttpClient, verilənlər bazası, SharedPreferences. Funksiya skopu: repozitoriyalar, Use Cases (əgər vəziyyəti yoxdursa). Transient: Value Objects, DateFormatter, parserlər. Skop səhvi — tez-tez rast gəlinən problem: ekran vəziyyətini saxlayan singleton sızmalara səbəb olur. Android Hilt-də @ActivityScoped bu problemi həll edir, Swinject-də — .container ehtiyatla.

Tez-tez verilən suallar

Sadəcə new ilə yaratmaq olarsa, DI nə üçün lazımdır?

new siniflər arasında sərt bağlılıq yaradır — kodu dəyişmədən tətbiqi əvəz edə bilməzsiniz. Test etmək çətindir: real servis əvəzinə mock qoya bilməzsiniz. SRP pozulur: sinif həm biznes məntiqinə, həm də asılılıqların yaradılmasına cavabdeh olur. DI bu problemləri asılılıqları xaricdən inject etməklə və abstraksiyalarla işləməklə həll edir.

Dagger Hilt yoxsa Koin — Android üçün nə seçməli?

Dagger Hilt — Google-dan standart, kodgenerasiya ilə compile-time DI, daha yaxşı performans və Jetpack ilə inteqrasiya. Koin — runtime DI, konfiqurasiyada daha sadə, lakin daha yavaş və runtime səhvləri ilə. İstehsalat layihələri üçün Hilt seçin. Koin prototiplər və kiçik tətbiqlər üçün uyğundur.

Swinject iOS üçün yeganə DI-dir?

Xeyr. iOS üçün mövcuddur: Swinject (runtime, məşhur), Needle (Uber-dən compile-time), Dip (yüngül), Weaver (Sourcery əsaslı). Apple daxili DI konteyneri təqdim etmir, lakin init vasitəsilə əl ilə inject etmə standart təcrübədir. SwiftUI üçün çox vaxt xarici kitabxanalar olmadan Environment və ya @StateObject vasitəsilə əl ilə DI kifayətdir.

DI-ni freymvorksuz istifadə etmək olar?

Bəli. Konstruktor vasitəsilə əl ilə inject etmə — freymvorksuz DI-dir. Service Locator — freymvorksuz alternativdir. Fabriklər və Factory Method — həmçinin DI formasıdır. Freymvork (Dagger, Swinject) rutin qeydiyyatı və asılılıqların həllini avtomatlaşdırır, lakin 10-20 sinif üçün əl ilə DI kifayətdir.

DI naxışdır yoxsa prinsip?

DI — Inversion of Control prinsipini tətbiq edən texnikadır (naxış). GoF naxışlarından fərqli olaraq, DI 3-4 sinifdən ibarət ciddi struktura malik deyil. DI asılılıqların təşkili üsuludur, dizayn naxışı deyil. DI konteynerləri (Dagger, Swinject) — bu texnikanı avtomatlaşdıran freymvorklərdir.

Nəticə

  • DI — asılılıqların konstruktor, setter və ya metod vasitəsilə xaricdən inject edilməsi texnikası
  • Dagger Hilt — Android üçün compile-time kodgenerasiyası və @HiltViewModel ilə DI standartı
  • Swinject — Container, Assembly və skoplarla iOS üçün runtime DI
  • Constructor Injection — məcburi asılılıqlar üçün üstünlük verilən üsul
  • Skop — vəziyyətsiz servislər üçün Singleton, ekran asılılıqları üçün funksiya skopu
  • Test etmə — DI kodu dəyişmədən asılılıqları mock ilə əvəz etməyi asanlaşdırır

Açar təslim mobil tətbiq hazırlayacağıq

IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.

Layihəni müzakirə et

Həm də oxuyun