โ† Kembali ke Konsep OOP
๐Ÿ”Œ
Prinsip

Dependency Inversion

Module tingkat tinggi tidak boleh bergantung langsung pada module tingkat rendah; keduanya harus bergantung pada abstraksi.

Masalahnya

Saat business logic tingkat tinggi langsung membuat dan bergantung pada class konkret tingkat rendah (misalnya email client tertentu), kodenya jadi kaku, sulit dites, dan terikat erat pada detail implementasi yang bisa berubah.

Cara Kerja

  • โ€บModule tingkat tinggi dan rendah sama-sama bergantung pada abstraksi bersama (interface/protocol)
  • โ€บImplementasi konkret "di-inject" ke module tingkat tinggi lewat constructor atau property injection
  • โ€บMemungkinkan pertukaran implementasi (misalnya sender asli vs. mock) tanpa mengubah business logic
  • โ€บMenjadi dasar framework dependency injection serta arsitektur berlapis yang mudah dites

Kapan Digunakan

โœ“Business logic perlu bisa dites dengan dependency yang di-mock
โœ“Mengganti infrastruktur (database, network client, file system) tanpa menyentuh logika domain
โœ“Memisahkan lapisan dalam clean architecture atau hexagonal architecture

Contoh di Dunia Nyata

๐ŸŽ
iOS

Meng-inject NetworkServiceProtocol ke ViewModel alih-alih memakai URLSession secara langsung

๐Ÿค–
Android

Hilt/Dagger meng-inject implementasi interface Repository ke dalam ViewModel

๐ŸŒ
Web

Mengoper interface database ke constructor service, yang bisa ditukar dengan implementasi in-memory saat testing

Contoh Kode

Swift
protocol MessageSender {
    func send(_ message: String)
}

struct EmailSender: MessageSender {
    func send(_ message: String) { print("Email: \(message)") }
}

struct SMSSender: MessageSender {
    func send(_ message: String) { print("SMS: \(message)") }
}

// High-level module depends on the abstraction, not a concrete sender
struct NotificationService {
    let sender: MessageSender

    func notify(_ message: String) {
        sender.send(message)
    }
}

let service = NotificationService(sender: SMSSender())
service.notify("Your order has shipped")