DIP: alapok, függőségek invertálása a fejlesztésben

Szerző: IT Sectr Megjelenés: 2026-05-12 Olvasási idő: 9 perc

DIP (Dependency Inversion Principle) — az SOLID ötödik elve, amely meghatározza a modulok közötti függőségek felépítésének szabályait: a magasabb szintű modulok nem függhetnek az alacsonyabb szintű moduloktól, mindkettőnek absztrakcióktól kell függenie. Az absztrakciók nem függhetnek a részletektől — a részleteknek az absztrakcióktól kell függeniük. Ezt az elvet Robert Martin írta le a Clean Architecture (2017) című könyvében, amely a lazán kapcsolódó architektúra alapját képezi. A könyv adatai szerint a függőségek invertálásának elve megszünteti a merev kapcsolatokat az alkalmazás rétegei között.

Fontos

  • DIP — a függőségek invertálásának elve, ötödik az SOLID-ban, az architekturális határokról
  • Magasabb szintű modulok nem importálhatnak alacsonyabb szintű modulokat — csak absztrakciókat
  • DIP ≠ DI: Dependency Inversion — architekturális elv, Dependency Injection — megvalósítási módja
  • Az absztrakciók a magasabb szintű modulhoz tartoznak, a megvalósítások pedig az alacsonyabb szintűhöz
  • DIP megfordítja a függőségek hagyományos hierarchiáját a többrétegű architektúrákban

Mi az a DIP (Dependency Inversion Principle)?

DIP (Dependency Inversion Principle) — a függőségek invertálásának elve, amely megfordítja a hagyományos felfogást a modulok közötti függőségek irányáról. A magas szintű modulok (üzleti logika) nem függhetnek közvetlenül az alacsony szintű moduloktól (adatbázis, hálózat, UI). Ehelyett mindkét szint a magasabb szintű modulban meghatározott absztrakcióktól függ.

A DIP formális megfogalmazása két szabályt tartalmaz: A — a magasabb szintű modulok nem függhetnek az alacsonyabb szintű moduloktól, mindkettőnek absztrakcióktól kell függenie. B — az absztrakciók nem függhetnek a részletektől, a részleteknek az absztrakcióktól kell függeniük. A második szabály az első következménye: ha az absztrakció a részletektől függ, nem lehet stabil alap a magasabb szintű modul számára.

DIP nélkül a tipikus architektúra így néz ki: BusinessLogic → DatabaseRepository — az üzleti logika közvetlenül egy konkrét repozitóriumtól függ. DIP-pel: BusinessLogic → DatabaseServiceInterface ← DatabaseRepository. A BusinessLogic nem tud a DatabaseRepository létezéséről, csak a DatabaseService interfészt ismeri, amelyet az üzleti logikán kívül implementálnak.

Függőségek iránya a DIP-ben

Az invertálás azt jelenti, hogy a vezérlés áramlása és a függőségek áramlása ellentétes irányba mutat. A vezérlés áramlása felülről lefelé halad: UI → ViewModel → UseCase → Repository. A függőségek áramlása alulról felfelé halad: A Repository implementálja a UseCase-ben meghatározott interfészt. A Repository (alacsonyabb szint) függ a UseCase-től (magasabb szint).

Ez az invertálás a legfontosabb különbség a DIP és a szokásos rétegekre bontás között. A hagyományos réteges architektúrában minden réteg az alatta lévő rétegtől függ. A DIP-pel rendelkező architektúrában minden réteg absztrakcióktól függ, és ezen absztrakciók megvalósítása az infrastruktúra rétegben található, amely DI mechanizmusokon keresztül csatlakozik a felső rétegekhez.

Hogyan működik a függőségek invertálásának elve

A DIP mechanizmusa az absztrakciók magasabb szintű modulokban történő meghatározásával és azok alacsonyabb szintű modulokban történő megvalósításával valósul meg. A magasabb szintű modul deklarál egy interfészt a szükséges funkcióhoz. Az alacsonyabb szintű modul implementálja ezt az interfészt. Az összeillesztés (wiring) az alkalmazás kompozíciós gyökerének szintjén történik.

A DIP meglévő kódba történő bevezetésének folyamata: emelje ki az interfészt az alacsony szintű modul számára, helyezze át ezt az interfészt a magasabb szintű modulba (vagy egy külön absztrakciós rétegbe), írja át a felső modul függőségét az interfészre, kényszerítse az alacsony szintű modult az interfész implementálására. Ezen lépések után a függőség iránya az ellenkezőjére változott.

A DIP megköveteli a kompozíciós gyökér mechanizmusát — az alkalmazás azon pontját, ahol az összes függőség létrejön és összekapcsolódik. Androidban ez az Application.get() vagy a Hilt komponens, iOS-ben — az AppDelegate vagy SceneDelegate. A kompozíciós gyökér az egyetlen hely, ahol a kód ismeri a konkrét megvalósításokat.

Rétegek elkülönítése DIP-pel

A DIP architekturális határokat képez az alkalmazás rétegei között. Amikor a ViewModel függ a UserRepository interfésztől, a presentation és domain réteg között határ jön létre: a ViewModel (presentation) nem tudja, honnan származnak az adatok. Ez a határ lehetővé teszi a UserRepository megvalósításának megváltoztatását (Room → REST → Mock) anélkül, hogy befolyásolná a ViewModel-t. Minél több ilyen határ van, annál ellenállóbb az alkalmazás a keretrendszerek és könyvtárak változásaival szemben.

A Google által ajánlott Android architektúrában a DIP a UseCase-eken keresztül valósul meg, amelyek a domain rétegben találhatók és a Repository interfészektől függenek. A RepositoryImpl a data rétegben található és implementálja ezeket az interfészeket. A presentation réteg (ViewModel) a UseCase-től függ. A függőségek iránya a presentation-től a domain felé, a domain-től a data felé halad — de egyik réteg sem ismeri a másik réteg konkrét megvalósításait.

A DIP és a DI (Dependency Injection) közötti különbség

A DIP-et és a DI-t gyakran összekeverik, de különböző fogalmak. DIP — architekturális elv (MIT kell tenni: absztrakcióktól függeni). DI — megvalósítási minta (HOGYAN kell ezt tenni: függőségek átadása a konstruktoron keresztül). A DIP arra a kérdésre válaszol, hogy mire kell a moduloknak épülniük?, a DI arra, hogy hogyan kapják meg az objektumok a függőségeiket?.

Dependency Injection — a függőségek objektumba történő befecskendezésének módja konstruktoron, metóduson vagy tulajdonságon keresztül. Amikor egy Kotlin osztályban a Repository interfész a konstruktoron keresztül kerül átadásra — ez DI. És az a tény, hogy a ViewModel osztály a Repository interfésztől függ, nem a konkrét RoomRepository megvalósítástól — ez DIP. DI — eszköz, DIP — cél.

A DIP betartható DI keretrendszer nélkül is: a függőségek manuális összekapcsolása a kompozíciós gyökérben szintén DI (manual DI). Használható DI keretrendszer (Dagger, Hilt, Koin) a DIP megsértése mellett: ha a ViewModel közvetlenül new() segítségével hoz létre Repository objektumot — a DIP sérül, még akkor is, ha a keretrendszer telepítve van. DIP — architekturális döntés, DI — technikai részlet.

DIP példák a mobilfejlesztésben

Vizsgáljuk meg a Android példát a DIP adatrétegre történő alkalmazására. DIP nélkül a ViewModel közvetlenül létrehozza a RoomDatabase-t és a DAO-t. DIP-pel — a ViewModel a UserRepository interfésztől függ, és a konkrét RoomUserRepository megvalósítás kívülről kerül biztosításra.

kotlin
// Az absztrakció a domain réteghez tartozik (magasabb szint)
interface UserRepository {
    fun getUser(id: Int): User
}

// A domain réteg csak az absztrakciótól függ
class GetUserUseCase(
    private val repo: UserRepository
) {
    fun execute(id: Int): User = repo.getUser(id)
}

// A data rétegben lévő megvalósítás a domain réteg absztrakciójától függ
class RoomUserRepository(
    private val dao: UserDao
) : UserRepository {
    override fun getUser(id: Int): User {
        return dao.getById(id)
    }
}

// Kompozíciós gyökér
class AppModule {
    fun provideUserRepository(dao: UserDao): UserRepository {
        return RoomUserRepository(dao)
    }
}

iOS példa Application Coordinator és protokoll használatával a navigációhoz:

swift
// Navigációs absztrakció a domain rétegben
protocol AuthNavigation {
    func navigateToHome()
    func navigateToLogin()
}

// A ViewModel az absztrakciótól függ, nem az UIKit-től
final class AuthViewModel {
    private let navigation: AuthNavigation

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

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

// Coordinator (UIKit réteg) implementálja a domain réteg protokollját
final class AppCoordinator: AuthNavigation {
    func navigateToHome() {
        // UIKit navigációs kód
    }
    func navigateToLogin() {
        // UIKit navigációs kód
    }
}

Kulcsfontosságú pont: Az AuthViewModel (domain) nem tud az AppCoordinator (UIKit) létezéséről. Csak az AuthNavigation protokollt ismeri. Ha holnap az UIKit-et lecserélik SwiftUI-ra — az AuthViewModel nem igényel változtatásokat. A DIP függetlenné teszi a domain réteget a keretrendszerektől és UI könyvtáraktól.

Eszközök a DIP-hez: Dagger, Hilt, Koin

Hilt — a szabványos DI eszköz Androidhoz, amelyet a Google ajánl. Beépítve a Jetpackbe, támogatja a ViewModel-t, Fragment-et, Service-t és más Android komponenseket. A Hilt automatizálja a kompozíciós gyökér létrehozását @Module, @Provides, @Inject annotációkon keresztül. A Hilt használata nem garantálja a DIP betartását — a UserRepository interfészt a domain rétegben kell meghatározni, nem a data rétegben.

Koin — könnyűsúlyú DI keretrendszer Kotlinhoz kódgenerálás és annotáció-feldolgozó nélkül. A Koin DSL (module, single, factory) könnyebben megtanulható, de a függőségek ellenőrzése futásidőben történik, nem fordítási időben. A Koin népszerű a többplatformos projektekben (KMP) az iOS támogatásnak köszönhetően.

Dagger 2 — a Hilt elődje, még mindig használják nagy projektekben. A Dagger fordítási időben generál DI kódot, ami maximális teljesítményt és hibadiagnosztikát biztosít a build szakaszban. A Hilt a Daggerre épül és egyszerűsített API-t nyújt. Új projektekhez a Google a Hilt-et ajánlja elsődleges DI keretrendszerként.

DI modulok szervezése rétegek szerint

A DI moduloknak meg kell felelniük az architekturális rétegeknek, és DomainModule, DataModule, PresentationModule részekre kell osztani őket. A DomainModule csak absztrakciókat és UseCase-eket biztosít. A DataModule megvalósításokat biztosít az absztrakciókhoz. A PresentationModule összeköti a ViewModel-t a UseCase-szel. Ez a szervezés garantálja, hogy a domain réteg független marad az infrastrukturális könyvtáraktól.

A DI keretrendszerek közötti migráció során (például Koin-ról Hilt-re) a DomainModule szerkezete nem változik — csak a DataModule és PresentationModule összekapcsolási módszerei változnak. A DIP biztosítja a domain logika elkülönítését, a DI keretrendszer pedig a technikai összekapcsolási mechanizmus.

Gyakran Ismételt Kérdések

Mindig alkalmazni kell a DIP-et?

DIP szükséges az architekturális határokon — az alkalmazás rétegei között (domain → data, presentation → domain). Egy rétegen belül a DIP felesleges lehet. Például egy StringFormatter segédosztály a domain rétegen belül nem igényel interfészt — ha nincsenek előfeltételek a lecserélésére.

A DIP ugyanaz, mint a függőségbefecskendezés?

Nem. DIP — elv: a moduloknak absztrakcióktól kell függeniük. DI — minta: az objektum kívülről kapja a függőségeit, nem maga hozza létre azokat. A DI a DIP megvalósításának módja, de a DIP betartható DI nélkül is (gyárak vagy szolgáltatás-lokátor segítségével). A DI DIP nélkül lehetséges, de nincs architekturális értéke.

Hol kell meghatározni az interfészeket a DIP-hez?

Az interfészek ahhoz a modulhoz tartoznak, amelyik használja azokat, nem ahhoz, amelyik implementálja őket. A UserRepository a domain rétegben kerül deklarálásra és a data rétegben implementálásra. Ez a DIP legfontosabb szabálya: az absztrakció tulajdonosa a fogyasztó, nem a megvalósítás szolgáltatója.

Hogyan befolyásolja a DIP a tesztelést?

A DIP lehetővé teszi a tesztelést elkülönített rétegeken. A UserRepository-tól (interfész) függő ViewModel mock megvalósítással tesztelhető adatbázis nélkül. DIP nélkül a ViewModel a RoomUserRepository-tól függne, és minden teszthez adatbázis konfigurációra lenne szükség. A DIP + DI teljes modul-elkülönítést biztosít a tesztelés során.

Melyik DI keretrendszert válasszam Androidhoz?

Hilt — a szabványos választás Android projektekhez, a Google ajánlásával. Koin — alternatíva Kotlin Multiplatform projektekhez. Dagger 2 — meglévő projektekhez, ahol a Hilt-re való migráció indokolatlan. A keretrendszer kiválasztása nem szünteti meg a DIP architekturális szintű betartásának szükségességét.

Összefoglalás

  • DIP (Dependency Inversion Principle) — az SOLID ötödik elve az architekturális határokról absztrakciókon keresztül
  • Magasabb szintű modulok nem függnek az alacsonyabb szintű moduloktól — mindkettő absztrakcióktól függ
  • DIP ≠ DI: elv kontra megvalósítási minta; DI — mód, DIP — cél
  • Az absztrakciók a fogyasztóhoz tartoznak (domain réteg), nem a szolgáltatóhoz (data réteg)
  • Kompozíciós gyökér — az egyetlen hely az alkalmazásban, ahol a konkrét függőségek összegyűlnek
  • Hilt, Koin, Dagger — DI eszközök, amelyek automatizálják az összekapcsolást, de nem helyettesítik a DIP architekturális döntést
  • A domain réteg DIP szerint felépítve független marad a keretrendszerektől, UI-tól és infrastrukturális könyvtáraktól

Kulcsrakész mobilalkalmazást fejlesztünk

Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.

Projekt megbeszélése

Olvassa el is