---
title: 'Bağımlılık Enjeksiyonu'
source: 'https://academia.sh/tr/kurslar/ios-uretim/bagimlilik-enjeksiyonu'
course: 'Mimari, Test ve Yayınlama'
language: tr
updated: '2026-10-21T05:03:07+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Bağımlılığı içeride kurmakla dışarıdan vermenin denetime etkisini ayırt eder'
    - 'Gerçek ağ istemcisine bağlı bir denetimin neden iki sürümde de kaldığını ölçer'
    - 'Boş yanıt dönen bir güdükle kusuru yakalayan denetimi kurar'
    - 'Sahte bağımlılığın hangi yolu taklit ettiğinin denetimi ayıran ya da kör yaptığını gösterir'
---

# Bağımlılık Enjeksiyonu

Bir nesnenin ihtiyaç duyduğu bağımlılığı kendisi kurmak yerine dışarıdan alması ve sahte bağımlılığın neyi taklit ettiğinin denetimin ayrımını nasıl belirlediği.

Üçüncü derste durumu tek bir yere topladık ve indirgeyiciyi ekransız ölçtük. İndirgeyici dışarıdan hiçbir şey almadığı için ölçmek kolaydı. Çoğu nesne böyle değildir: bir ürün listesi görünüm modeli ürünleri bir ağ istemcisinden alır, bir sipariş nesnesi kaydı bir depodan okur. Bu bağımlılıklar nesnenin içinde kuruluyorsa nesneyi denetlemek, bağımlılığı da çalıştırmak demektir.

Bağımlılık enjeksiyonu bu bağı tersine çevirir: nesne ihtiyaç duyduğu bağımlılığı kendisi kurmaz, dışarıdan alır. Böylece denetimde gerçek bağımlılığın yerine ona benzeyen bir sahte bağımlılık verilebilir. Bu dersin kusuru ürün listesindedir: istemci boş bir yanıt aldığında önceki listeyi korumak yerine listeyi temizliyor. Çekirdek önceki derslerdeki gibi birebir gelir.

```swift
// Separation.swift — bir denetimin dogru surumu kusurlu surumden ayirip ayirmadigini siniflandirir
struct Contrast<Output: CustomStringConvertible> {
    let label: String
    let correct: Output
    let faulty: Output
    let passesCorrect: Bool
    let passesFaulty: Bool

    var separation: String {
        switch (passesCorrect, passesFaulty) {
        case (true, false): return "ayiran"
        case (true, true): return "kor"
        case (false, false): return "gurultulu"
        case (false, true): return "ters"
        }
    }

    func describe() -> String {
        let onCorrect = passesCorrect ? "gecti" : "kaldi"
        let onFaulty = passesFaulty ? "gecti" : "kaldi"
        print("\(label) | dogru=\(correct)/\(onCorrect) kusurlu=\(faulty)/\(onFaulty) -> \(separation)")
        return separation
    }
}

func contrast<Output>(_ label: String, correct: Output, faulty: Output, check: (Output) -> Bool) -> Contrast<Output> {
    Contrast(label: label, correct: correct, faulty: faulty, passesCorrect: check(correct), passesFaulty: check(faulty))
}

func countSeparations(_ results: [String]) {
    for name in ["ayiran", "kor", "gurultulu", "ters"] {
        print("\(name): \(results.filter { $0 == name }.count)")
    }
}
```

Bu derste de çekirdek yalnız kullanılır. Ayrım adları aynıdır ve yalnız denetimlerin ne yaptığı değişir.

## Bağımlılığı Dışarıdan Vermek

Enjeksiyonsuz görünüm modeli, istemcisini kendisi kurar. Bu sürümde denetim için iki yol vardır ve ikisi de kötüdür: gerçek ağa çıkmak ya da kurulan istemciyi değiştirmenin bir yolunu bulmak.

```swift
import Foundation

// Bagimlilik iceride kuruluyor — KAYNAK
final class ProductListViewModel {
    private let service = NetworkProductService(session: URLSession.shared)
    private(set) var items: [String] = []

    func refresh() async {
        guard let reply = await service.fetch() else { return }
        items = reply
    }
}
```

Bu parça çalıştırılmaz; oturum ve ağ platforma bağlıdır ve ders için çıktı iddia etmez. Yapısı yeter: `service` sabit olarak içeride kurulmuştur. Bu modelin `refresh` çağrısını denetlemek için ağın açık olması, sunucunun o anki yanıtı vermesi ve yanıtın denetimin beklediği olması gerekir. Üçü de denetimin elinde değildir.

Dışarıdan vermenin üç yolu vardır. En doğrudan olanı başlatıcı enjeksiyonudur: bağımlılık nesne kurulurken parametre olarak gelir. Özellik enjeksiyonunda nesne kurulduktan sonra atanır; işlev parametresi enjeksiyonunda yalnız o işlevin ihtiyacı olan çağrıda verilir. Bu derste başlatıcı kullanılır, çünkü bağımlılık nesnenin ömrü boyunca değişmez.

```swift
import Foundation

// Bagimlilik disaridan veriliyor — KAYNAK
protocol ProductService: Sendable {
    func fetch() async -> [String]?
}

struct NetworkProductService: ProductService {
    let session: URLSession

    func fetch() async -> [String]? {
        guard let url = URL(string: "https://example.com/products") else { return nil }
        guard let (data, _) = try? await session.data(from: url) else { return nil }
        return try? JSONDecoder().decode([String].self, from: data)
    }
}

final class InjectedListViewModel {
    private let service: any ProductService
    private(set) var items: [String] = []

    init(service: any ProductService) { self.service = service }
}
```

Bu parça da çalıştırılmaz. Önemli olan protokoldür: görünüm modeli artık gerçek istemciyi değil, `ProductService` sözleşmesini tanır. Gerçek istemci o sözleşmeye uyar, denetimdeki sahte bağımlılık da uyar. Görünüm modeli ikisini ayırt edemez.

## Sahte Bağımlılık Neyi Taklit Eder

Model, protokolü ve iki sahte bağımlılığı saf Swift'le kurar. `OfflineService` ağın olmadığı durumu taklit eder: her çağrıda `nil` döner. `StubService` önceden verilen bir yanıtı döner; bu, çağrıldığında sabit yanıt veren bir güdüktür. Ürün listesi modeli `refresh` içinde üç yoldan birine girer: istek hata verirse liste korunur; yanıt boşsa doğru sürüm de listeyi korur, kusurlu sürüm listeyi temizler; yanıt doluysa liste yanıtla değişir. Kusur yalnız ikinci yoldadır. `Catalog.swift` bir yardımcı dosyadır ve çıktı vermez.

```swift
// Catalog.swift — urun listesi modeli ve sahte bagimliliklar
protocol ProductService: Sendable {
    func fetch() async -> [String]?
}

struct OfflineService: ProductService {
    func fetch() async -> [String]? { nil }
}

struct StubService: ProductService {
    let reply: [String]
    func fetch() async -> [String]? { reply }
}

struct ProductListModel {
    private(set) var items: [String]
    let service: any ProductService
    let faulty: Bool

    mutating func refresh() async {
        guard let reply = await service.fetch() else { return }
        if reply.isEmpty && !faulty { return }
        items = reply
    }
}

func refreshedItems(start: [String], service: any ProductService, faulty: Bool) async -> String {
    var model = ProductListModel(items: start, service: service, faulty: faulty)
    await model.refresh()
    return model.items.isEmpty ? "bos" : model.items.joined(separator: ",")
}
```

Üç denetim aynı iki sürüme uygulanır. Birincisi gerçek ağ istemcisine bağlı denetimin yerini tutar. Sandbox'ta ağ kapalı olduğu için bu denetimde istemci `OfflineService` ile modellenir ve denetim sunucunun üç ürünlük listesini bekler. İkincisi boş yanıt dönen bir güdükle çalışır. Üçüncüsü ağ hatasını taklit eder ve listenin korunmasını bekler.

```swift
// main.swift — uc denetim, uc farkli sahte bagimlilik
let start = ["defter", "kalem"]

let goodReal = await refreshedItems(start: start, service: OfflineService(), faulty: false)
let badReal = await refreshedItems(start: start, service: OfflineService(), faulty: true)
let goodEmpty = await refreshedItems(start: start, service: StubService(reply: []), faulty: false)
let badEmpty = await refreshedItems(start: start, service: StubService(reply: []), faulty: true)

let real = contrast("gercek-ag-istemcisi", correct: goodReal, faulty: badReal) { $0 == "defter,kalem,silgi" }.describe()
let empty = contrast("bos-yanit-guduk", correct: goodEmpty, faulty: badEmpty) { $0 == "defter,kalem" }.describe()
let failure = contrast("hata-yaniti-guduk", correct: goodReal, faulty: badReal) { $0 == "defter,kalem" }.describe()

countSeparations([real, empty, failure])
```

```text
gercek-ag-istemcisi | dogru=defter,kalem/kaldi kusurlu=defter,kalem/kaldi -> gurultulu
bos-yanit-guduk | dogru=defter,kalem/gecti kusurlu=bos/kaldi -> ayiran
hata-yaniti-guduk | dogru=defter,kalem/gecti kusurlu=defter,kalem/gecti -> kor
ayiran: 1
kor: 1
gurultulu: 1
ters: 0
```

Çıktı Swift 6.4'te sandbox'ta üç kez koşturuldu ve üçünde de aynıdır. Üç denetim üç ayrı ayrımı verdi: bir ayıran, bir kör, bir gürültülü.

`gercek-ag-istemcisi` sunucudan gelecek üç ürünlük listeyi bekler. Sandbox'ta ağ olmadığı için istemci yanıt veremez, liste olduğu gibi kalır ve iki sürümde de `defter,kalem` çıkar. Denetim iki sürümde de kalır, yani gürültülüdür. Kusuru yakalamaz; kalması kusurdan değil, denetimin ağa bağlı olmasındandır. Aynı gürültü gerçek bir ortamda da vardır: sunucu yavaşsa, erişilemiyorsa ya da başka bir yanıt veriyorsa denetim kırmızı olur ve kırmızıya bakan kişi kusuru koda değil, ağa yorar.

`bos-yanit-guduk` listeyi koruyup korumadığına bakar ve güdük boş bir yanıt döner. Doğru sürüm `defter,kalem` verir ve geçer; kusurlu sürüm listeyi temizler, `bos` verir ve kalır. Denetim ayıran olur. Burada fark bağımlılığın dışarıdan verilmesidir: kusurun yaşadığı yol, yani boş yanıt, ancak bağımlılığı kontrol eden bir denetimle tetiklenir.

`hata-yaniti-guduk` ağ hatasını taklit eder ve listenin korunmasını bekler. Hata dalında iki sürüm de listeyi korur, bu yüzden denetim iki sürümde de geçer ve kördür. Kusur boş yanıt dalındadır, hata dalında değil. Sahte bağımlılık yanlış yolu taklit ettiğinde denetim doğru görünür ve kusuru kaçırır.

## Varsayımlar

**MM1.** Gerçek ağ istemcisi ölçülmez. Sandbox'ta ağ kapalıdır; gerçek istemci yerine `OfflineService` ile "yanıt gelmiyor" durumu modellenir ve bu durumun denetimi gürültülüdür. Gerçek bir ortamda aynı denetimin nasıl davranacağı sunucuya bağlıdır ve bu ders ona bir değer vermez.

**MM2.** `ProductService` protokolünün modeldeki hâli gerçek istemcinin sözleşmesiyle aynıdır: bir yanıt listesi ya da `nil`. Gerçek istemcide hata türleri, kimlik doğrulama ve yeniden deneme vardır; model bunları tek bir `nil`'e indirger.

**MM3.** Yanıt bir ürün adları listesidir. Sayfalama, sıralama ve ürün alanları modele girmez.

## Sınırlayıcı Ölçüm

Sahte bağımlılığın neyi taklit ettiği, ayrımı belirler. Güdük her çağrıda aynı dolu yanıtı dönerse kusurlu yol hiç koşmaz. Aynı iki sürüm iki güdükle denenir: biri hep üç ürünlük dolu yanıt döner, öteki boş yanıt döner.

```swift
// main.swift — ayni iki surum, iki farkli guduk
let start = ["defter", "kalem"]
let full = ["defter", "kalem", "silgi"]

let goodFull = await refreshedItems(start: start, service: StubService(reply: full), faulty: false)
let badFull = await refreshedItems(start: start, service: StubService(reply: full), faulty: true)
let goodNone = await refreshedItems(start: start, service: StubService(reply: []), faulty: false)
let badNone = await refreshedItems(start: start, service: StubService(reply: []), faulty: true)

let onFull = contrast("dolu-yanit-guduk", correct: goodFull, faulty: badFull) { $0 == "defter,kalem,silgi" }.describe()
let onNone = contrast("bos-yanit-guduk", correct: goodNone, faulty: badNone) { $0 == "defter,kalem" }.describe()

countSeparations([onFull, onNone])
```

```text
dolu-yanit-guduk | dogru=defter,kalem,silgi/gecti kusurlu=defter,kalem,silgi/gecti -> kor
bos-yanit-guduk | dogru=defter,kalem/gecti kusurlu=bos/kaldi -> ayiran
ayiran: 1
kor: 1
gurultulu: 0
ters: 0
```

Üç koşumda çıktı aynıdır. Güdük hep dolu yanıt döndüğünde iki sürüm de listeyi yanıtla değiştirir ve denetim kördür; kusurun yaşadığı yol hiç çalışmaz. Güdük boş yanıt döndüğünde aynı denetim türü ayıran olur. Denetim değişmedi, sahte bağımlılığın taklit ettiği yanıt değişti. Bu, sahte bağımlılığın en sık yapılan hatasına işaret eder: güdüğü mutlu yol için kurmak. Mutlu yolda hiçbir şey kırılmaz, denetim yeşil kalır ve kusurlu yol bir sonraki gerçek boş yanıtta kullanıcıya ulaşır.

## Özet

- Bağımlılık enjeksiyonu nesnenin ihtiyaç duyduğu bağımlılığı dışarıdan almasıdır; en yaygın yolu başlatıcıdan vermektir.
- Gerçek ağ istemcisine bağlı denetim ağa ve sunucuya bağlıdır; ağ yanıt vermeyince iki sürümde de kalır ve gürültülüdür.
- Boş yanıt dönen bir güdükle denetim, kusurun yaşadığı yolu tetikler ve ayıran olur.
- Hata yanıtını taklit eden güdük, kusur boş yanıt dalındaysa kördür: üç denetim bir ayıran, bir kör, bir gürültülü verdi.
- Güdük hep dolu yanıt dönerse kusurlu yol koşmaz ve denetim kör kalır.
- Bu dersin ayrımı: **gürültülü**.

## Sonraki Adım

Bağımlılığı dışarıdan vermek, nesneleri birbirinden ayırdı; ama hepsi aynı hedefte duruyorsa her nesne her nesneyi görebilir. Beşinci derste Modülerleştirme, kodu paketlere böler ve hangi paketin hangisine bağlanabileceğini kurala bağlar. Orada kusur bir özellik modülünün başka bir özellik modülüne doğrudan bağlanması olacak, ve derlemenin geçip geçmediğine bakan denetim bu kusura karşı kör kalacak.
