---
title: 'Bağımlılık Enjeksiyonu'
source: 'https://academia.sh/tr/kurslar/tasarim-kaliplari/bagimlilik-enjeksiyonu'
course: 'Tasarım Kalıpları'
language: tr
updated: '2026-08-17T18:10:38+00:00'
license: 'CC BY-SA 4.0'
---

# Bağımlılık Enjeksiyonu

Yaratım sorumluluğunun kullanan koddan alınıp bileşim köküne taşınması: bağımlılıklarını kendisi kuran ücret modülü ile bağımlılıklarını çağrıda alan sürümün doğrudan bağ sayısı ve ithal kapanışı boyutuyla karşılaştırılması, indirim politikası değiştiğinde düzenlenen dosyanın sayılması ve kapanışın yok olmayıp yer değiştirdiğinin gösterilmesi.

Bir önceki ders kayıt için örnek üreten sürümü sınamalardan geçirdi, ama kaydı kullanan modülün
o örneği nereden alacağını söylemedi. Modül `yeniKayit` çağrısını kendi gövdesinde yaparsa her
modül kendi kaydını kurar; modül tekil sürümü ithal ederse küresel durum geri gelir. Üçüncü
seçenek nesneyi hiç kurmamaktır.

**Bağımlılık enjeksiyonu** Frontend Mimarisi ve Birim Testi kurslarında tanımlanmış bir
düzenektir; burada yeniden tanımlanmaz. Bu dersin sorduğu şey daha dar: bir modülün
bağımlılığını kendisi kurması ile dışarıdan alması arasındaki fark hangi sayılarla görünür.
Ölçüler Tasarım İlkeleri kursundan gelir: bir modülün **doğrudan bağ sayısı** ve **ithal
kapanışı boyutu**.

## Sağlayıcı Modüller

İki sürüm de aynı üç parçayı kullanır: tarife nesneleri, onları tutan bir kayıt ve bir indirim
politikası. Bu parçalar iki sürüm için ortaktır.

```js
// saglayici/tarife.mjs — iki tarife nesnesi
export const STANDART = { ad: "standart", taban: (g) => 3900 + Math.ceil(g.agirlik) * 900 };
export const EKSPRES = { ad: "ekspres", taban: (g) => 6400 + Math.ceil(g.agirlik) * 1500 };
```

```js
// saglayici/kayit.mjs — tarife kaydi; iki tarife yuklenmis bir ornek uretir
import { STANDART, EKSPRES } from "./tarife.mjs";

export class TarifeKaydi {
  #tarifeler = new Map();

  kaydet(tarifeNesnesi) {
    this.#tarifeler.set(tarifeNesnesi.ad, tarifeNesnesi);
    return this;
  }

  tarife(ad) {
    const t = this.#tarifeler.get(ad);
    if (t === undefined) throw new RangeError(`bilinmeyen tarife: ${ad}`);
    return t;
  }
}

export const yeniKayit = () => new TarifeKaydi().kaydet(STANDART).kaydet(EKSPRES);
```

```js
// saglayici/indirim.mjs — iki indirim politikasi
export const sozlesmeliIndirim = (gonderi, ham) =>
  gonderi.sozlesmeNo === null ? 0 : Math.round(ham * 0.12);

export const indirimsiz = () => 0;
```

## Bağımlılığını Kendisi Kuran Modül

Birinci sürümde ücret modülü ihtiyacı olan her şeyi kendisi ithal eder ve kaydı kendisi kurar.
Çağıran taraf yalnız `ucret` işlevini görür.

```js
// kendi/ucret.mjs — bagimliliklarini kendisi ithal eder ve kendisi kurar
import { yeniKayit } from "../saglayici/kayit.mjs";
import { sozlesmeliIndirim } from "../saglayici/indirim.mjs";

const BOLGE = { "34": 100, "06": 115, "65": 140 };
const KAYIT = yeniKayit();

export function ucret(gonderi) {
  const taban = KAYIT.tarife(gonderi.tarife).taban(gonderi);
  const ham = Math.round((taban * (BOLGE[gonderi.adres.slice(0, 2)] ?? 160)) / 100);
  return ham - sozlesmeliIndirim(gonderi, ham);
}
```

```js
// kendi/kur.mjs — cagiran yalniz ucret islevini ithal eder
import { ucret } from "./ucret.mjs";

export const ORNEK = [
  { tarife: "standart", agirlik: 2.4, adres: "34100", sozlesmeNo: "S1042" },
  { tarife: "ekspres", agirlik: 2.4, adres: "06500", sozlesmeNo: null },
];

for (const g of ORNEK) console.log(`kendi   ${g.tarife.padEnd(9)} ${ucret(g)} kurus`);
```

```sh
node kendi/kur.mjs
```

```
kendi   standart  5808 kurus
kendi   ekspres   12535 kurus
```

Bu sürümün çağrı yeri en kısa olanıdır: tek bir ithal ve tek bir çağrı. Bedeli çağrı yerinde
değil, ücret modülünün kime bağlandığında.

## Bağımlılığını Çağrıda Alan Modül

İkinci sürümde ücret modülü hiçbir şey ithal etmez. Kayıt ve indirim politikası çağrıda verilir;
modül bir hesap işlevi üretir.

```js
// enjekte/ucret.mjs — bagimliliklari cagrida alir; hicbir modul ithal etmez
const BOLGE = { "34": 100, "06": 115, "65": 140 };

export const ucretHesabiYap = ({ kayit, indirim }) => (gonderi) => {
  const taban = kayit.tarife(gonderi.tarife).taban(gonderi);
  const ham = Math.round((taban * (BOLGE[gonderi.adres.slice(0, 2)] ?? 160)) / 100);
  return ham - indirim(gonderi, ham);
};
```

Kurulum işi bileşim köküne geçer. Kök parçaları kurar, hangi indirim politikasının
kullanılacağını seçer ve hesabı üretir.

```js
// enjekte/kur.mjs — bilesim koku: parcalari kurar, secer ve enjekte eder
import { yeniKayit } from "../saglayici/kayit.mjs";
import { sozlesmeliIndirim, indirimsiz } from "../saglayici/indirim.mjs";
import { ucretHesabiYap } from "./ucret.mjs";

const POLITIKA = { sozlesmeli: sozlesmeliIndirim, yok: indirimsiz };
const secim = process.argv[2] ?? "sozlesmeli";
const ucret = ucretHesabiYap({ kayit: yeniKayit(), indirim: POLITIKA[secim] });

export const ORNEK = [
  { tarife: "standart", agirlik: 2.4, adres: "34100", sozlesmeNo: "S1042" },
  { tarife: "ekspres", agirlik: 2.4, adres: "06500", sozlesmeNo: null },
];

for (const g of ORNEK)
  console.log(`enjekte ${secim.padEnd(11)} ${g.tarife.padEnd(9)} ${ucret(g)} kurus`);
```

```sh
node enjekte/kur.mjs
node enjekte/kur.mjs yok
```

```
enjekte sozlesmeli  standart  5808 kurus
enjekte sozlesmeli  ekspres   12535 kurus
enjekte yok         standart  6600 kurus
enjekte yok         ekspres   12535 kurus
```

İlk iki satır birinci sürümün çıktısıyla birebir aynı. Son iki satır indirim politikası
değiştirildiğinde ne olduğunu gösteriyor: sözleşmeli gönderinin ücreti 5808 kuruştan 6600 kuruşa
çıktı, sözleşmesiz gönderinin ücreti değişmedi. İki koşum arasında hiçbir dosya düzenlenmedi.

## Bağların ve Kapanışın Sayılması

Aşağıdaki ölçer verilen modüllerin doğrudan ithal ettiği modül sayısını ve ithal kapanışının
boyutunu verir. Kapanış, modülün ulaştığı bütün modüllerin geçişli kümesidir.

```js
// kapanis.mjs — verilen modullerin dogrudan bag sayisi ve ithal kapanisi boyutu
import { readFileSync } from "node:fs";
import { dirname, resolve } from "node:path";

const ITHAL = /(?:from|import)\s*["'](\.[^"']+)["']/g;
const baglar = (yol) => [...readFileSync(yol, "utf8").matchAll(ITHAL)].map((m) => m[1]);

function kapanis(yol, gorulen = new Set()) {
  for (const bag of baglar(yol)) {
    const hedef = resolve(dirname(yol), bag);
    if (!gorulen.has(hedef)) {
      gorulen.add(hedef);
      kapanis(hedef, gorulen);
    }
  }
  return gorulen;
}

for (const yol of process.argv.slice(2))
  console.log(`${yol.padEnd(18)} dogrudan-bag=${baglar(yol).length} ` +
    `ithal-kapanisi=${kapanis(yol).size}`);
```

```sh
node kapanis.mjs kendi/ucret.mjs enjekte/ucret.mjs kendi/kur.mjs enjekte/kur.mjs
```

```
kendi/ucret.mjs    dogrudan-bag=2 ithal-kapanisi=3
enjekte/ucret.mjs  dogrudan-bag=0 ithal-kapanisi=0
kendi/kur.mjs      dogrudan-bag=1 ithal-kapanisi=4
enjekte/kur.mjs    dogrudan-bag=3 ithal-kapanisi=4
```

İlk iki satır kazancı veriyor. Ücret modülünün doğrudan bağ sayısı 2'den 0'a, ithal kapanışı
boyutu 3'ten 0'a indi. Sıfır kapanış, modülün metninde hiçbir somut ayrıntının adının geçmediği
anlamına gelir: kayıt sınıfının adı, tarife nesnelerinin adı ve indirim işlevinin adı ücret
modülünde yoktur.

Son iki satır kalıbın gerçekte ne yaptığını söylüyor. İki sürümün toplam kapanışı da 4'tür.
Yani kapanış yok olmadı, **yer değiştirdi**: birinci sürümde ücret modülünün üstündeydi, ikinci
sürümde bileşim kökünün. Bileşim kökünün doğrudan bağ sayısı 1'den 3'e çıktı. Yaratım
sorumluluğunun taşınması ölçüde tam olarak böyle görünür — bir modülün kazandığı bağımsızlık
başka bir modülün kazandığı bağ sayısıdır.

## Politika Değiştiğinde Düzenlenen Dosya

İkinci ölçü şu istekle çıkar: indirim politikası kaldırılacak. Aşağıdaki betik iki ağacı
kopyalar, isteği ikisine de uygular, sonuçları koşturur ve düzenlenen dosyaları sayar. Yerinde
düzenlemede yedek uzantısı verilmiştir; GNU ve BSD `sed` bu biçimde aynı davranır.

```sh
cp -r kendi kendi-yok
cp -r enjekte enjekte-yok
sed -i.y 's/sozlesmeliIndirim/indirimsiz/g' kendi-yok/ucret.mjs
rm -f kendi-yok/*.y
node kendi-yok/kur.mjs
node enjekte-yok/kur.mjs yok

for p in kendi enjekte; do
  echo "$p duzenlenen dosya = $(diff -rq $p $p-yok | grep -c '^Files ')"
  echo "$p ucret modulu degisti mi = $(diff -q $p/ucret.mjs $p-yok/ucret.mjs > /dev/null && echo hayir || echo evet)"
done
```

```
kendi   standart  6600 kurus
kendi   ekspres   12535 kurus
enjekte yok         standart  6600 kurus
enjekte yok         ekspres   12535 kurus
kendi duzenlenen dosya = 1
kendi ucret modulu degisti mi = evet
enjekte duzenlenen dosya = 0
enjekte ucret modulu degisti mi = hayir
```

İlk dört satır iki ağacın aynı ücretleri ürettiğini gösteriyor; ilk iki satırdaki `kendi`
etiketi modül metninde sabit olduğu için kopyada da öyle kalmıştır. Son dört satır ölçüyü
veriyor: birinci sürümde istek karşılanırken bir dosya düzenlendi ve o dosya ücret modülünün
kendisiydi. İkinci sürümde hiçbir dosya düzenlenmedi; politika seçimi zaten bileşim kökünün
dışında, çalışma anında verilen bir değerdi.

Bu, Açık–Kapalı İlkesi dersindeki ayrımın aynısıdır ve son satır belirleyici olandır: ücret
hesabını yapan modül birinci sürümde değişti, ikincisinde değişmedi.

## Bedel

Kalıbın bedeli üç yerde. Birincisi çağrı yerinin uzunluğu: ücret hesabını elde etmek artık tek
bir ithal değil, üç ithal ve bir kurulum çağrısıdır. İkincisi bileşim kökünün doğrudan bağ
sayısının 1'den 3'e çıkmasıdır; bileşim kökü kaçınılmaz olarak her somut parçayı bilen tek
modüldür ve büyümesi bileşen sayısıyla doğru orantılıdır.

Üçüncüsü okunurluk cinsinden değil, izlenmesi gereken çağrı sayısı cinsindendir. Birinci sürümde
`ucret` işlevinin hangi indirim politikasını kullandığı aynı dosyada, ithal satırında yazılıdır.
İkinci sürümde bu bilgi ücret modülünde yoktur; onu bulmak için bileşim kökü okunmalı ve orada
`POLITIKA` tablosunun hangi girdisinin seçildiği izlenmelidir. Yani bir sorunun cevabı bir
dosyadan iki dosyaya yayılmıştır.

Bu yüzden bağımlılık enjeksiyonu, bağımlılığın gerçekten değişebilir olduğu yerlerde uygulanır.
Bölge tablosu iki sürümde de ücret modülünün içinde bırakılmıştır: bir sabit tablodur, süreç
boyunca değişmez ve dışarıdan verilmesi kapanışı düşürmez, yalnız bileşim kökünü büyütür.

## Özet

- Bağımlılık enjeksiyonu yaratım sorumluluğunu nesneyi kullanan koddan alır ve bileşim köküne
  taşır; kalıbın sorusu nesnenin nasıl kurulduğu değil, kimin kurduğudur.
- Ücret modülünün doğrudan bağ sayısı 2'den 0'a, ithal kapanışı boyutu 3'ten 0'a indi; modülün
  metninde hiçbir somut parçanın adı geçmez.
- Toplam kapanış iki sürümde de 4'tür: kapanış yok olmadı, bileşim köküne taşındı ve kökün
  doğrudan bağ sayısı 1'den 3'e çıktı.
- İndirim politikası kaldırıldığında birinci sürümde 1 dosya düzenlendi ve o dosya ücret
  modülüydü; ikinci sürümde 0 dosya düzenlendi.
- Bedel çağrı yerinin uzaması, bileşim kökünün bileşen sayısıyla büyümesi ve bir bağımlılığın
  hangi somut parça olduğunun ancak bileşim kökü okunarak bulunmasıdır.

## Sonraki Adım

Bu konudaki beş düzenek bir nesnenin kim tarafından ve nasıl kurulacağı sorusunu çözdü: tür
seçiminin bir yere toplanması, birbirine uyan parçaların birlikte üretilmesi, çok adımlı
kurulumun sıraya sokulması, var olan bir nesnenin kopyalanması, tek örneğin nerede tutulacağı ve
kurma işinin bileşim köküne taşınması. Hepsinde ortak bir varsayım vardı: kurulan nesne
kitaplığın kendi nesnesiydi ve arayüzünü de kitaplık yazıyordu, dolayısıyla parçaların
birbirine uyması sağlanabilir bir şeydi. Sonraki konu nesnelerin **nasıl birleştirileceğini**
düzenler ve ilk problem tam bu varsayımın düştüğü yerdir: şeklini kitaplığın belirlemediği bir
arayüzle çalışmak. Her dış taşıyıcı sağlayıcısı kendi sözlüğünü, kendi birimini ve hata
bildirmenin kendi biçimini getirir; sonraki ders bu uyumsuzluğun tek bir yerde çözülmesini ele
alır ve kazancını yine dokunulan dosya sayısıyla ölçer.
