Not: İçeriğin orjinali ve alternatif dağıtımları hakkındaki bilgiler aşağıda paylaşılmıştır.
- YouTube (orjinal içerik)
- Blog (orjinal içerik temelli AI destekli)
- Podcast (orjinal içeriğe ait ses)
“High-level modules should not depend on low-level modules. Both should depend on abstractions.”
— Robert C. Martin
Bu yazıda SOLID prensiplerinin son adımı olan Dependency Inversion Principle (DIP) konusunu inceliyoruz.
Yani: Bağımlılık yönünü tersine çevirerek esnek ve sürdürülebilir bir mimari kurmak.
🧩 DIP Nedir?
Klasik bir sistemde üst seviye (high-level) sınıflar, alt seviye (low-level) sınıflara doğrudan bağımlıdır.
Bu durumda, alt katmandaki bir değişiklik yukarıdaki katmanları da etkiler.
DIP der ki:
Üst seviye modüller, alt seviye modüllere değil, soyutlamalara (interfaces) bağımlı olmalıdır.
⚠️ Sorun: Doğrudan Bağımlılık
class ReportRepository {
public String generateOrderReport() {
return "Order Report generated.";
}
}
class ReportService {
private final ReportRepository reportRepository;
public ReportService(ReportRepository reportRepository) {
this.reportRepository = reportRepository;
}
public void printReport() {
System.out.println(reportRepository.generateOrderReport());
}
}
Burada ReportService, doğrudan ReportRepository sınıfına bağımlı.
Yarın farklı bir veri kaynağı (örneğin Redis, MongoDB, PostgreSQL) kullanmak istersen, ReportService’i değiştirmek zorundasın.
Bu da DIP ihlali anlamına gelir.
✅ Çözüm: Abstraction (Interface) ile Bağımlılığı Tersine Çevir
DIP’nin temel fikri: Detaylar soyutlamaya bağlı olmalı, soyutlamalar detaya değil.
interface ReportRepository {
String generateOrderReport();
}
class PostgresReportRepository implements ReportRepository {
public String generateOrderReport() {
return "PostgreSQL report generated.";
}
}
class RedisReportRepository implements ReportRepository {
public String generateOrderReport() {
return "Redis report generated.";
}
}
Ve artık ReportService doğrudan bir sınıfa değil, interface’e bağımlı:
class ReportService {
private final ReportRepository repository;
public ReportService(ReportRepository repository) {
this.repository = repository;
}
public void printReport() {
System.out.println(repository.generateOrderReport());
}
}
Bu yapı sayesinde ReportService hangi repository’nin kullanıldığını bilmek zorunda değil.
Sadece ReportRepository arayüzünü tanıyor.
Yani bağımlılık yönü tersine çevrilmiş oldu.
🔁 Katmanlı Mimaride DIP
Katmanlı bir mimaride ideal bağımlılık yönü:
Controller → Service → Interface ← Repository Implementation
Servis, doğrudan repository’ye değil, repository interface’ine bağımlıdır.
Repository implementasyonu da bu interface’i uygular.
Böylece iki katman arasındaki bağ zayıf (loosely coupled) hale gelir.
🧠 DIP ve IoC (Inversion of Control)
DIP genellikle IoC (Inversion of Control) ve Dependency Injection (DI) kavramlarıyla birlikte kullanılır.
Örneğin Spring veya Guice gibi frameworkler, bağımlılıkları constructor üzerinden otomatik olarak enjekte eder:
@Service
public class ReportService {
private final ReportRepository repository;
@Autowired
public ReportService(ReportRepository repository) {
this.repository = repository;
}
}
Artık hangi repository implementasyonunun kullanılacağına framework karar verir — sen değil.
Bu da sistemin test edilebilirliğini ve esnekliğini büyük ölçüde artırır.
🧱 Gerçek Hayattan Analogi
Bir elektrikli cihazın prize takılan kablosunu düşün.
Cihaz, prizde ne tür bir enerji altyapısı olduğunu bilmez — sadece bir arayüz (interface) üzerinden enerji alır.
Aynı şekilde, sisteminin üst katmanları da alt katmanların detaylarına bağlı olmamalıdır.
🎬 Sonuç ve Serinin Sonu
Bu videoda Dependency Inversion Principle (DIP)’i inceledik.
Yüksek seviyeli modüllerin, düşük seviyeli detaylardan bağımsız olmasının neden hayati olduğunu gördük.
Böylece SOLID prensiplerinin beşini de tamamlamış olduk 🎯
Artık elinizde sürdürülebilir, genişletilebilir ve bakımı kolay sistemler kurmak için güçlü bir temel var.
🎥 Videoyu izlemek istersen: