Mobil Geliştirmede Bağımlılık (Coupling) — Temel Kavramlar, Türler ve Nasıl Azaltılır

Yazar: IT Sectr Yayınlanma: 2026-05-13 Okuma süresi: 9 dk

Bağımlılık (Coupling), bir uygulama modülünün başka bir modüle ne kadar bağımlı olduğunu gösteren bir metriktir. Wikipedia'ya göre, gevşek bağımlılık (low coupling), modüllerin komşularını bozmadan değiştirilebildiği iyi tasarlanmış bir sistemin işaretidir. Mobil uygulamalar tasarlarken bağımlılığı yönetmek, mimarın ana görevlerinden biridir.

Ana Noktalar

  • Coupling — modüller arası bağımlılık derecesi: yüksek = sıkı bağımlılık, düşük = gevşek bağımlılık
  • Content coupling — en kötü tür, bir modülün başka bir modülün iç verilerini değiştirmesi
  • Data coupling — en iyi tür, modüllerin parametreler aracılığıyla yalnızca basit veri alışverişi yapması
  • Dependency Injection — mobil geliştirmede bağımlılığı azaltmanın ana aracı
  • Arayüzler ve soyutlamalar — uygulama katmanları arasındaki bağımlılığı azaltmanın ana mekanizması

Coupling Nedir

Coupling (bağımlılık), bir modülün veya sınıfın başka bir modüle ne kadar sıkı bağlı olduğunu belirleyen bir metriktir. Bir modül diğerinin iç yapısı hakkında ne kadar çok şey bilirse, coupling o kadar yüksek ve sistemi değiştirmek o kadar zor olur. İyi tasarlanmış bir mimaride coupling minimum düzeyde olmalıdır — modüller yalnızca kesin olarak tanımlanmış arayüzler aracılığıyla etkileşime girer.

Coupling'in iki yönü vardır: afferent (gelen bağımlılıklar — kaç modül buna bağımlı) ve efferent (giden bağımlılıklar — bu modül kaç modüle bağımlı). Bu metrikleri analiz etmek, bir modüldeki değişikliğin diğer birçok modülü etkileyeceği mimari sıcak noktaları belirlemeye yardımcı olur. IntelliJ Dependency Analyzer ve Xcode Graph gibi araçlar bu bağlantıları görselleştirir.

Sıfır coupling'in imkansız olduğunu anlamak önemlidir — modüllerin bir şekilde etkileşime girmesi gerekir, aksi takdirde bu bir sistem değil, izole programlar topluluğudur. Mimarın görevi, coupling'i yönetilebilir ve şeffaf hale getirmektir. İdeal: modüller yalnızca arayüzler aracılığıyla etkileşime girer ve yalnızca basit veri iletir, birbirlerinin iç yapısını bilmez. Buna gevşek bağımlılık (loose coupling) denir.

Zayıftan Güçlüye Bağımlılık Türleri

Altı tür coupling, en iyiden en kötüye doğru bir ölçek oluşturur. Bu ölçeği anlamak, mevcut kodu değerlendirmeye ve yeniden düzenleme yönünü seçmeye yardımcı olur. Çoğu mobil projede karışık coupling türleri bulunur ve mimarın görevi, güçlü türleri kademeli olarak zayıf türlerle değiştirmektir.

Data coupling — en iyi tür

Data coupling (veri bağımlılığı) — modüller yöntem parametreleri aracılığıyla yalnızca basit veri alışverişi yapar. Modül A, Modül B'nin yöntemini çağırır, ilkel türler veya basit yapılar iletir ve bir sonuç alır. Modül A, B'nin dahili olarak nasıl uygulandığını bilmez. Bu en arzu edilen coupling türüdür: değişikliklerin etkisini en aza indirir.

Örnek: EmailValidator.isValid(email: String): Boolean. Tüketici sınıf bir dize iletir ve bir Boolean alır, doğrulayıcının içindeki normal ifadeler veya doğrulama kuralları hakkında hiçbir fikri olmaz. Doğrulama mantığını değiştirmek, tüketiciyi değiştirmeyi gerektirmez — coupling minimumdur. Data coupling, bir uygulamadaki tüm genel arayüzlerin hedefidir.

Stamp coupling — kabul edilebilir ancak ideal değil

Stamp coupling (damga bağımlılığı) — modüller bileşik nesneleri değiş tokuş eder ancak alanlarının yalnızca bir kısmını kullanır. Modül A, calculateDiscount yöntemine bir User nesnesi iletir, bu yöntem yalnızca user.status'u kullanır. Sorun: User yapısı değişirse (zorunlu bir alan eklenirse), calculateDiscount modülü değişmez, ancak User nesnesini oluşturan tüketici değişir.

Uygulamada, damga bağımlılığı kaçınılmazdır ve iletilen nesne standart bir veri modeli (Entity) ise kabul edilebilir. Sorun, bir modülün yalnızca tek bir alan için bütün bir nesne alması durumunda ortaya çıkar. Bu gibi durumlarda, belirli değeri doğrudan iletmek daha iyidir (data coupling). Çözüm, alan kullanımını alıcı taraf açısından analiz etmektir.

Control, External, Common ve Content coupling

Control coupling — bir modül diğerine davranışını kontrol eden bir bayrak iletir (calculate(useNewAlgorithm: Boolean)). Bu, stamp coupling'ten daha kötüdür çünkü tüketici modül, çağrılan modülün dahili çalışma varyantlarını bilmelidir. Çözüm: yöntemi ikiye bölmek — calculateWithNewAlgorithm() ve calculateWithLegacyAlgorithm().

External coupling — modüller harici bir protokole, veri formatına veya API'ye bağımlıdır. Aynı JSON'u ayrıştıran veya aynı veritabanıyla çalışan tüm modüller external coupling'e sahiptir. Tamamen önlenemez ancak izole edilebilir: harici format ile dahili modeller arasında bir eşleme katmanı oluşturun. Common coupling — modüller ortak bir global durumu paylaşır. Content coupling — en kötü tür, bir modülün başka bir modülün dahili verilerini doğrudan değiştirmesi.

Coupling türüSeviyeAçıklama
DataEn iyiParametrelerle basit veri iletimi
StampKabul edilebilirKısmi kullanımla nesne iletimi
ControlOrtaBayraklarla davranış kontrolü
ExternalYüksekHarici protokol/forma bağımlılık
CommonÇok yüksekGlobal durum paylaşımı
ContentKabul edilemezModül iç verilerinin doğrudan değiştirilmesi

Data (ideal) ile content (felaket) arasındaki coupling ölçeği, kod incelemeleri için pratik bir araçtır. Bir projede common veya content coupling görürseniz — bunlar öncelikli yeniden düzenleme hedefleridir. Data ve stamp coupling kabul edilebilir ve her projede bulunur, ancak miktarları kontrol edilmelidir.

Mobil Geliştirmede Coupling Neden Kritiktir

Yüksek coupling, geliştirmeyi yavaş bir sürece dönüştürür ve her değişiklik düzinelerce potansiyel olarak bozuk modülün kontrol edilmesini gerektirir. Bu özellikle mobil geliştirmede kritiktir: platformlar yıllık (Android API Level, iOS SDK), kütüphaneler üç aylık ve iş gereksinimleri sürekli güncellenir. Gevşek bağımlılık, sürekli gerilemeler olmadan bu değişiklik akışıyla başa çıkmanın tek yoludur.

Pratik örnek: tüm ekranların doğrudan NetworkingManager ve DatabaseManager'ı içe aktardığı bir mobil uygulama. HTTP istemcisini Retrofit'ten Ktor'a (Android) veya URLSession'dan Alamofire'a (iOS) değiştirirken geliştirici her ekranı değiştirmek zorunda kalır. Düşük coupling ile, NetworkDataSource arayüzünün arkasında gizli bir uygulamayı değiştirmek yeterlidir — tüketiciler değişimi fark etmez.

Coupling'in birim test üzerindeki etkisi de çok büyüktür. Yüksek coupling'e sahip bir sınıf (doğrudan kurucuda bağımlılıklar oluşturma) izole olarak test edilemez — veritabanını, ağı ve kullanıcı arayüzünü de beraberinde sürükler. Böyle bir sınıfı test etmek için bir emülatör başlatmanız ve entegrasyon testlerini beklemeniz gerekir. Düşük coupling'e sahip bir sınıf, kurucu enjeksiyonu yoluyla bağımlılıkları kabul eder ve kolayca mock'lanabilir.

kotlin
// Yüksek coupling — sınıf kendi bağımlılıklarını kendisi oluşturur
class ProfileViewModelHigh {
    private val api = RetrofitApi()
    private val db = RoomDatabase.getInstance()
    private val cache = MemoryCache()
}

// Düşük coupling — bağımlılıklar kurucu aracılığıyla iletilir
class ProfileViewModelLow(
    private val api: ApiService,
    private val db: DatabaseService,
    private val cache: CacheService
)

İlk durumda, ProfileViewModelHigh belirli uygulamalara sıkı sıkıya bağlıdır — Retrofit'i Ktor ile değiştirmek ViewModel kodunun değiştirilmesini gerektirir. İkinci durumda, ProfileViewModelLow yalnızca arayüzlere bağımlıdır ve uygulamaları dışarıdan sağlanır. İkinci sınıfı test etmek çok kolaydır: mock uygulamaları iletin ve emülatör olmadan mantığı doğrulayın.

Coupling'i Azaltma Desenleri

Bağımlılığı Tersine Çevirme İlkesi (SOLID'de D), coupling'i azaltmanın temelidir. İlke, somut uygulamalara değil soyutlamalara bağımlı olmayı emreder. Bir sınıfın doğrudan bir RetrofitApi nesnesi oluşturması yerine, bir ApiService arayüzü alması gerekir. Bu, bağımlılığı belirli bir kütüphaneden soyutlama seviyesine kaydırır ve tüketiciyi değiştirmeden değiştirilebilir.

Observer deseni (veya reaktif versiyonları — StateFlow, Combine Publishers), veri kaynağı ve aboneler arasındaki coupling'i azaltır. Abone, verilerin nereden geldiğini bilmez — sadece değişikliklere tepki verir. Bu, göndericiyi ve alıcıyı birbirinden ayırır: mevcut aboneleri değiştirmeden yeni bir veri kaynağı eklenebilir. EventBus ve SharedFlow aynı prensipte çalışır.

Bridge deseni, soyutlamayı uygulamadan ayırarak bağımsız olarak değişmelerine olanak tanır. Mobil geliştirmede Bridge, örneğin platforma bağımlı modüller için kullanılır: iOS (Kingfisher, Nuke) ve Android (Glide, Coil) için farklı uygulamalara sahip ortak bir ImageLoader arayüzü. ImageLoader ile çalışan kod, seçilen kütüphaneye bağımlı değildir ve uygulamayı basitçe değiştirerek onu değiştirebilir.

Bağımlılık Yönetim Aracı Olarak Dependency Injection

Dependency Injection (DI), mobil geliştirmede coupling'i azaltmanın en pratik aracıdır. Bir sınıfın kendi bağımlılıklarını oluşturması yerine, bir DI kapsayıcısı (Android için Hilt, Koin, Dagger; iOS için Swinject, Factory) bunları dışarıdan sağlar. Sınıf, bağımlılıkları kurucu, yöntem veya özellik enjeksiyonu yoluyla alır, somut uygulamalar hakkında bilgi sahibi olmaz.

DI, sınıf bağımlılıklarını açıkça belgeler: sınıf hangi modüllerle etkileşime girdiğini anlamak için kurucuya bakmak yeterlidir. Kurucu farklı katmanlardan 8 parametre alıyorsa — bu aşırı coupling'in işaretidir ve yeniden düzenleme gerektirir. İyi uygulama, sınıf başına 3-4'ten fazla bağımlılık olmamasıdır. Daha fazlası, Tek Sorumluluk İlkesi ihlali ve aşırı coupling'i gösterir.

DI ayrıca test etmeyi de basitleştirir: her test için mock bağımlılıklarla bir sınıf oluşturursunuz, gerçek veritabanı veya ağ gerekmez. Flutter'da DI, Provider, Riverpod veya GetIt aracılığıyla uygulanır. Framework'ten bağımsız olarak hedef birdir: bağımlılıkları açık ve değiştirilebilir hale getirerek modüller arasındaki bağımlılığı azaltmak. Mobil projelerde DI kullanımı 2020'lerden bu yana fiili standarttır.

swift
// DI kapsayıcısı bağımlılık grafiğini oluşturur
protocol AuthServiceProtocol {
    func login(email: String, password: String) async throws -> User
}

final class AuthService: AuthServiceProtocol {
    func login(email: String, password: String) async throws -> User {
        // uygulama
    }
}

// ViewModel belirli hizmeti bilmez — yalnızca protokol
final class LoginViewModel {
    private let auth: AuthServiceProtocol

    init(auth: AuthServiceProtocol) {
        self.auth = auth
    }
}

// DI Container, somut türlerin oluşturulduğu tek yerdir
final class DIContainer {
    lazy var authService: AuthServiceProtocol = AuthService()
    lazy var loginViewModel: LoginViewModel {
        LoginViewModel(auth: self.authService)
    }
}

Burada LoginViewModel belirli bir AuthService'e değil, yalnızca AuthServiceProtocol protokolüne bağımlıdır. Uygulamayı değiştirmek (örneğin, Firebase Auth'tan özel bir sunucuya geçiş) yalnızca DIContainer'da değişiklik gerektirir. AuthServiceProtocol'ün tüm tüketicileri etkilenmez — bağımlılık soyutlama ve DI aracılığıyla en aza indirilir.

Sıkça Sorulan Sorular

Coupling, cohesion'dan nasıl farklıdır?

Cohesion bir modülün iç tutarlılığını ölçerken, coupling modüller arasındaki dış bağlantıyı ölçer. İyi bir mimari, yüksek cohesion ve düşük coupling hedefler. Bu metrikler ters orantılıdır: cohesion'ı artırmak genellikle coupling'i azaltır ve bunun tersi de geçerlidir.

Üretim kodunda hangi coupling türü kabul edilebilir?

Data ve stamp normaldir ve her projede bulunur. Control coupling sınırlı senaryolarda kabul edilebilir (örneğin, strategy deseni). External coupling, harici API'lerle çalışırken kaçınılmazdır ancak bir eşleme katmanının arkasında izole edilmelidir. Common ve content coupling, acil yeniden düzenleme gerektiren mimari sorunların işaretleridir.

Bir projede coupling nasıl ölçülür?

Statik analiz araçları: IntelliJ IDEA Dependency Matrix, Xcode Graph, Gradle Bağımlılık raporu, SonarQube. Metrikler: afferent coupling (Ca), efferent coupling (Ce), Kararsızlık (Ce/(Ca+Ce)). Yüksek Kararsızlık (1'e yakın), modülün değiştirilmesinin kolay olduğu ve az sayıda şeyin ona referans verdiği anlamına gelir — bu iyidir.

Düşük coupling zararlı olabilir mi?

Aşırı düşük coupling, kod gezinmesini zorlaştıran aşırı sayıda soyutlama ve arayüz anlamına gelebilir. Her sınıf için ayrı bir arayüz oluşturulursa, programcı dosyalar arasında geçiş yaparak zaman kaybeder. Denge: modülün harici API'si için arayüzler, ancak her dahili yardımcı sınıf için değil.

Eski kodla çalışırken coupling nasıl azaltılır?

Strangler Fig tekniğini kullanın — doğrudan çağrıları kademeli olarak arayüzlerle değiştirin. En çok referans verilen sınıflar için arayüz çıkararak başlayın. Ardından bir DI kapsayıcısı ekleyin. İzole edilmiş kodu karakterizasyon testleriyle kapsayarak yeniden düzenlemenin sistem davranışını değiştirmediğinden emin olun.

Özet

  • Coupling — modüller arası bağımlılık metriği: gevşek bağımlılık iyi mimarinin hedefidir
  • Data coupling — en iyi tür, content coupling — en kötü, üretim kodunda kabul edilemez
  • Bağımlılığı Tersine Çevirme ve arayüzler — bağımlılığı azaltmanın ana mekanizmaları
  • Dependency Injection — bağımlılıkları açık ve değiştirilebilir yapan pratik araç
  • Yüksek coupling kodu kırılgan yapar: bir değişiklik birçok modülü bozar
  • Düşük coupling testi basitleştirir: her modül emülatör olmadan bağımsızca mock'lanır
  • Dengeleyin — aşırı arayüzler kodu karmaşıklaştırır

Anahtar teslim bir mobil uygulama geliştireceğiz

IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.

Projeyi tartış

Ayrıca okuyun