DIP: mga batayan, inversion ng dependencies sa pag-develop

May-akda: IT Sectr Nai-publish: 2026-05-12 Oras ng pagbabasa: 9 min

DIP (Dependency Inversion Principle) — ang ikalimang prinsipyo ng SOLID na tumutukoy sa mga patakaran ng pagbuo ng mga dependency sa pagitan ng mga module: ang mga module ng mas mataas na antas ay hindi dapat umasa sa mga module ng mas mababang antas, pareho ay dapat umasa sa mga abstraction. Ang mga abstraction ay hindi dapat umasa sa mga detalye — ang mga detalye ay dapat umasa sa mga abstraction. Ang prinsipyong ito, na inilarawan ni Robert Martin sa Clean Architecture (2017), ay nasa pundasyon ng maluwag na magkakaugnay na arkitektura. Ayon sa aklat na ito, ang prinsipyo ng inversion ng dependencies ay nag-aalis ng matigas na mga ugnayan sa pagitan ng mga layer ng application.

Mga Pangunahing Punto

  • DIP — prinsipyo ng inversion ng dependencies, ikalima sa SOLID, tungkol sa mga hangganang arkitektural
  • Mga module ng mas mataas na antas ay hindi dapat mag-import ng mga module ng mas mababang antas — mga abstraction lamang
  • DIP ≠ DI: Dependency Inversion — prinsipyong arkitektural, Dependency Injection — paraan ng pagpapatupad nito
  • Ang mga abstraction ay pag-aari ng module ng mas mataas na antas, at ang mga implementasyon ay pag-aari ng mas mababang antas
  • DIP ay binabaligtad ang tradisyonal na hierarkiya ng mga dependency sa mga multi-layered na arkitektura

Ano ang DIP (Dependency Inversion Principle)?

DIP (Dependency Inversion Principle) — prinsipyo ng inversion ng dependencies na binabaligtad ang tradisyonal na pananaw tungkol sa direksyon ng mga dependency sa pagitan ng mga module. Ang mga module ng mataas na antas (business logic) ay hindi dapat direktang umasa sa mga module ng mababang antas (database, network, UI). Sa halip, ang parehong antas ay umaasa sa mga abstraction na tinukoy sa module ng mas mataas na antas.

Ang pormal na pormulasyon ng DIP ay may dalawang patakaran: A — ang mga module ng mas mataas na antas ay hindi dapat umasa sa mga module ng mas mababang antas, pareho ay dapat umasa sa mga abstraction. B — ang mga abstraction ay hindi dapat umasa sa mga detalye, ang mga detalye ay dapat umasa sa mga abstraction. Ang pangalawang patakaran ay bunga ng una: kung ang abstraction ay umaasa sa mga detalye, hindi ito maaaring maging matatag na pundasyon para sa module ng mas mataas na antas.

Kung walang DIP, ang tipikal na arkitektura ay ganito: BusinessLogic → DatabaseRepository — ang business logic ay direktang umaasa sa isang konkretong repository. Sa DIP: BusinessLogic → DatabaseServiceInterface ← DatabaseRepository. Ang BusinessLogic ay hindi alam ang pagkakaroon ng DatabaseRepository, alam lamang nito ang interface ng DatabaseService na ipinapatupad sa labas ng business logic.

Direksyon ng mga dependency sa DIP

Ang inversion ay nangangahulugan na ang daloy ng kontrol at daloy ng mga dependency ay nakadirekta sa magkasalungat na direksyon. Ang daloy ng kontrol ay mula sa itaas pababa: UI → ViewModel → UseCase → Repository. Ang daloy ng mga dependency ay mula sa ibaba pataas: Repository ay nag-iimplementa ng interface na tinukoy sa UseCase. Repository (mababang antas) ay umaasa sa UseCase (mataas na antas).

Ang inversion na ito ay ang pangunahing pagkakaiba sa pagitan ng DIP at ordinaryong paghahati sa mga layer. Sa tradisyonal na layered na arkitektura, bawat layer ay umaasa sa layer sa ibaba. Sa arkitektura na may DIP, lahat ng layer ay umaasa sa mga abstraction, at ang implementasyon ng mga abstraction na ito ay nasa layer ng imprastraktura na kumokonekta sa mga itaas na layer sa pamamagitan ng mga mekanismo ng DI.

Paano gumagana ang prinsipyo ng inversion ng dependencies

Ang mekanismo ng DIP ay naisasakatuparan sa pamamagitan ng pagtukoy ng mga abstraction sa mga module ng mas mataas na antas at pagpapatupad ng mga ito sa mga module ng mas mababang antas. Ang module ng mas mataas na antas ay nagdedeklara ng interface para sa kinakailangang functionality. Ang module ng mas mababang antas ay nag-iimplementa ng interface na ito. Ang pag-assemble (wiring) ay nagaganap sa antas ng composition root ng application.

Ang proseso ng paglalapat ng DIP sa umiiral na code: i-extract ang interface para sa module ng mababang antas, ilipat ang interface na ito sa module ng mas mataas na antas (o sa isang hiwalay na layer ng abstraction), isulat muli ang dependency ng itaas na module sa interface, pilitin ang module ng mababang antas na i-implement ang interface na ito. Pagkatapos ng mga hakbang na ito, ang direksyon ng dependency ay nagbago sa kabaligtaran.

Ang DIP ay nangangailangan ng mekanismo ng composition root — punto sa application kung saan ang lahat ng dependency ay nilikha at pinag-uugnay. Sa Android ito ay Application.get() o Hilt component, sa iOS — AppDelegate o SceneDelegate. Ang composition root ay ang tanging lugar kung saan alam ng code ang mga konkretong implementasyon.

Paghiwalay ng mga layer sa pamamagitan ng DIP

Ang DIP ay bumubuo ng mga hangganang arkitektural sa pagitan ng mga layer ng application. Kapag ang ViewModel ay umaasa sa interface ng UserRepository, isang hangganan ang lumilitaw sa pagitan ng presentation at domain layer: ViewModel (presentation) ay hindi alam kung saan nagmumula ang data. Ang hangganan na ito ay nagpapahintulot na baguhin ang implementasyon ng UserRepository (Room → REST → Mock) nang hindi naaapektuhan ang ViewModel. Kung mas marami ang mga hangganang ito, mas matatag ang application sa mga pagbabago ng framework at library.

Sa arkitektura ng Android na inirerekomenda ng Google, ang DIP ay ipinapatupad sa pamamagitan ng UseCase na nasa domain layer at umaasa sa mga interface ng Repository. Ang RepositoryImpl ay nasa data layer at nag-iimplementa ng mga interface na ito. Ang presentation layer (ViewModel) ay umaasa sa UseCase. Ang direksyon ng mga dependency ay mula presentation patungong domain, mula domain patungong data — ngunit walang layer na alam ang mga konkretong implementasyon ng ibang layer.

Pagkakaiba ng DIP at DI (Dependency Injection)

Ang DIP at DI ay madalas na napagkakamalan, ngunit iba't ibang konsepto ang mga ito. DIP — prinsipyong arkitektural (ANO ang dapat gawin: umasa sa mga abstraction). DI — pattern ng implementasyon (PAANO ito gagawin: magpasa ng mga dependency sa pamamagitan ng constructor). Sinasagot ng DIP ang tanong na sa ano dapat ibatay ang mga module?, DI — paano nakukuha ng mga object ang kanilang mga dependency?.

Dependency Injection — paraan ng pag-inject ng mga dependency sa isang object sa pamamagitan ng constructor, method, o property. Kapag sa isang Kotlin class ang interface ng Repository ay ipinapasa sa pamamagitan ng constructor — ito ay DI. At ang katotohanan na ang ViewModel class ay umaasa sa interface ng Repository, hindi sa konkretong implementasyon ng RoomRepository — ito ay DIP. DI — kasangkapan, DIP — layunin.

Maaaring sundin ang DIP nang walang DI framework: ang manu-manong pag-uugnay ng mga dependency sa composition root ay DI rin (manual DI). Maaaring gumamit ng DI framework (Dagger, Hilt, Koin) habang lumalabag sa DIP: kung ang ViewModel ay direktang gumagawa ng Repository object sa pamamagitan ng new() — ang DIP ay nilabag, kahit na naka-install ang framework. DIP — desisyong arkitektural, DI — teknikal na detalye.

Mga halimbawa ng DIP sa mobile development

Tingnan natin ang halimbawa sa Android ng paglalapat ng DIP sa layer ng data. Kung walang DIP, ang ViewModel ay direktang gumagawa ng RoomDatabase at DAO. Sa DIP — ang ViewModel ay umaasa sa interface ng UserRepository, at ang konkretong implementasyon ng RoomUserRepository ay ibinibigay mula sa labas.

kotlin
// Ang abstraction ay pag-aari ng domain layer (mas mataas na antas)
interface UserRepository {
    fun getUser(id: Int): User
}

// Ang domain layer ay umaasa lamang sa abstraction
class GetUserUseCase(
    private val repo: UserRepository
) {
    fun execute(id: Int): User = repo.getUser(id)
}

// Ang implementasyon sa data layer ay umaasa sa abstraction ng domain layer
class RoomUserRepository(
    private val dao: UserDao
) : UserRepository {
    override fun getUser(id: Int): User {
        return dao.getById(id)
    }
}

// Composition root
class AppModule {
    fun provideUserRepository(dao: UserDao): UserRepository {
        return RoomUserRepository(dao)
    }
}

Halimbawa sa iOS na may Application Coordinator at protocol para sa navigation:

swift
// Abstraction ng navigation sa domain layer
protocol AuthNavigation {
    func navigateToHome()
    func navigateToLogin()
}

// Ang ViewModel ay umaasa sa abstraction, hindi sa UIKit
final class AuthViewModel {
    private let navigation: AuthNavigation

    init(navigation: AuthNavigation) {
        self.navigation = navigation
    }

    func onLoginSuccess() {
        navigation.navigateToHome()
    }
}

// Coordinator (UIKit layer) ay nag-iimplementa ng protocol ng domain layer
final class AppCoordinator: AuthNavigation {
    func navigateToHome() {
        // UIKit navigation code
    }
    func navigateToLogin() {
        // UIKit navigation code
    }
}

Mahalagang punto: Ang AuthViewModel (domain) ay hindi alam ang pagkakaroon ng AppCoordinator (UIKit). Alam lamang nito ang protocol na AuthNavigation. Kung bukas ay papalitan ang UIKit ng SwiftUI — ang AuthViewModel ay hindi nangangailangan ng pagbabago. Ginagawa ng DIP ang domain layer na independyente sa mga framework at UI library.

Mga tool para sa DIP: Dagger, Hilt, Koin

Hilt — ang karaniwang tool ng DI para sa Android, inirerekomenda ng Google. Naka-integrate sa Jetpack, sumusuporta sa ViewModel, Fragment, Service, at iba pang bahagi ng Android. Ina-automate ng Hilt ang paglikha ng composition root sa pamamagitan ng mga anotasyon na @Module, @Provides, @Inject. Ang paggamit ng Hilt ay hindi ginagarantiya ang pagsunod sa DIP — ang interface ng UserRepository ay dapat tukuyin sa domain layer, hindi sa data layer.

Koin — magaan na DI framework para sa Kotlin nang walang code generation at annotation processor. Ang DSL ng Koin (module, single, factory) ay mas madaling matutunan, ngunit ang pagsusuri ng mga dependency ay nangyayari sa runtime, hindi sa compile time. Ang Koin ay sikat sa mga multiplatform na proyekto (KMP) dahil sa suporta nito sa iOS.

Dagger 2 — hinalinhan ng Hilt, ginagamit pa rin sa malalaking proyekto. Ang Dagger ay gumagawa ng DI code sa compile time, na nagbibigay ng maximum na performance at error diagnosis sa build stage. Ang Hilt ay binuo sa ibabaw ng Dagger at nagbibigay ng pinasimpleng API. Para sa mga bagong proyekto, inirerekomenda ng Google ang Hilt bilang pangunahing DI framework.

Organisasyon ng mga DI module ayon sa layer

Ang mga DI module ay dapat tumugma sa mga arkitektural na layer at hatiin sa DomainModule, DataModule, PresentationModule. Ang DomainModule ay nagbibigay lamang ng mga abstraction at UseCase. Ang DataModule ay nagbibigay ng mga implementasyon para sa mga abstraction. Ang PresentationModule ay nag-uugnay ng ViewModel sa UseCase. Ang ganitong organisasyon ay ginagarantiya na ang domain layer ay nananatiling independyente mula sa mga library ng imprastraktura.

Sa paglipat sa pagitan ng mga DI framework (halimbawa, mula Koin patungong Hilt), ang istraktura ng DomainModule ay hindi nagbabago — tanging ang mga paraan ng pag-uugnay sa DataModule at PresentationModule ang nagbabago. Tinitiyak ng DIP ang paghihiwalay ng domain logic, at ang DI framework ay ang teknikal na mekanismo ng pag-uugnay.

Mga Madalas na Itanong

Kailangan bang palaging ilapat ang DIP?

DIP ay kinakailangan sa mga hangganang arkitektural — sa pagitan ng mga layer ng application (domain → data, presentation → domain). Sa loob ng isang layer, ang DIP ay maaaring labis. Halimbawa, ang utility class na StringFormatter sa loob ng domain layer ay hindi nangangailangan ng interface — kung walang mga dahilan para palitan ito.

Ang DIP ba ay pareho ng dependency injection?

Hindi. DIP — prinsipyo: ang mga module ay dapat umasa sa mga abstraction. DI — pattern: ang object ay tumatanggap ng mga dependency mula sa labas, hindi ito lumilikha ng mga ito mismo. Ang DI ay paraan ng pagpapatupad ng DIP, ngunit ang DIP ay maaaring sundin nang walang DI (sa pamamagitan ng mga factory o service locator). Ang DI nang walang DIP ay posible ngunit walang halagang arkitektural.

Saan dapat tukuyin ang mga interface para sa DIP?

Ang mga interface ay pag-aari ng module na gumagamit ng mga ito, hindi ng module na nag-iimplementa ng mga ito. Ang UserRepository ay idinedeklara sa domain layer at iniimplementa sa data layer. Ito ang pangunahing patakaran ng DIP: ang may-ari ng abstraction ay ang consumer, hindi ang provider ng implementasyon.

Paano naaapektuhan ng DIP ang pag-test?

Ginagawa ng DIP na posible ang pag-test sa mga isolated na layer. Ang ViewModel na umaasa sa UserRepository (interface) ay tinetest na may mock implementasyon nang walang database. Kung walang DIP, ang ViewModel ay aasa sa RoomUserRepository at mangangailangan ng database configuration para sa bawat test. Ang DIP + DI ay nagbibigay ng kumpletong paghihiwalay ng mga module sa pag-test.

Aling DI framework ang pipiliin para sa Android?

Hilt — karaniwang pagpili para sa mga proyekto ng Android, inirerekomenda ng Google. Koin — alternatibo para sa mga proyekto ng Kotlin Multiplatform. Dagger 2 — para sa mga umiiral na proyekto kung saan ang paglipat sa Hilt ay hindi makatwiran. Ang pagpili ng framework ay hindi nag-aalis ng pangangailangan na sundin ang DIP sa antas ng arkitektura.

Buod

  • DIP (Dependency Inversion Principle) — ikalimang prinsipyo ng SOLID tungkol sa mga hangganang arkitektural sa pamamagitan ng mga abstraction
  • Mga module ng mas mataas na antas ay hindi umaasa sa mga module ng mas mababang antas — pareho ay umaasa sa mga abstraction
  • DIP ≠ DI: prinsipyo laban sa pattern ng implementasyon; DI — paraan, DIP — layunin
  • Ang mga abstraction ay pag-aari ng consumer (domain layer), hindi ng provider (data layer)
  • Composition root — tanging lugar sa application kung saan ang mga konkretong dependency ay tinipon
  • Hilt, Koin, Dagger — mga tool ng DI na nag-a-automate ng pag-uugnay, ngunit hindi pinapalitan ang desisyong arkitektural na DIP
  • Ang domain layer na binuo ayon sa DIP ay nananatiling independyente sa mga framework, UI, at library ng imprastraktura

Gagawa kami ng mobile application na turnkey

Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.

Pag-usapan ang proyekto

Basahin din