---
title: Bağdaştırıcı
source: 'https://academia.sh/tr/kurslar/tasarim-kaliplari/bagdastirici'
course: 'Tasarım Kalıpları'
language: tr
updated: '2026-08-17T18:10:39+00:00'
license: 'CC BY-SA 4.0'
---

# Bağdaştırıcı

Uyumsuz arayüzlerin köprülenmesi: iki dış taşıyıcı sağlayıcısının sözlüğüne dokunan dosyaların çevirinin istemciye dağıldığı düzende ve bağdaştırıcı arkasındaki düzende sayılması, üçüncü sağlayıcı eklendiğinde düzenlenen dosya ile eklenen satırın karşılaştırılması ve kalıbın dosya, satır ve dolaylılık cinsinden bedelinin çıkarılması.

Yaratımsal kalıplar bir nesnenin **kim tarafından ve nasıl kurulacağı** sorusunu çözdü:
fabrika yöntemi tür seçimini bir yere topladı, kurucu çok adımlı kurulumu sıraya soktu,
bağımlılık enjeksiyonu yaratma sorumluluğunu nesneyi kullanan koddan aldı. Hepsinde kurulan
şey kitaplığın kendi nesnesiydi ve arayüzünü de kitaplık yazıyordu.

Bu konu nesnelerin **nasıl birleştirileceğini** düzenler. İlk problem, şeklini kitaplığın
belirlemediği bir arayüzle çalışmaktır: gönderi ücretlendirme kitaplığı kaç taşıyıcıyla
çalışacaksa o kadar dış sağlayıcıya bağlanır, her sağlayıcı kendi sözlüğünü, kendi birimini
ve "bu gönderiyi taşımıyorum" demenin kendi biçimini getirir. **Bağdaştırıcı** (adapter) bu
uyumsuzluğu tek bir yerde çözer. Kazancı iki sayıyla ölçülür: sağlayıcı sözlüğüne dokunan
dosya sayısı ve üçüncü bir sağlayıcı eklendiğinde düzenlenen var olan dosya sayısı.

## Problem: İki Sözlük, İki Hata Biçimi

İki dış sağlayıcı aynı işi yapar ve hiçbir noktada uyuşmaz. Birincisi ağırlığı gram, ücreti
kuruş olarak alır ve kademe dışında `null` döndürür.

```js
// saglayici/kara.mjs — dis saglayici: gram ve kurus ile calisir, kademe disinda null doner
const KADEME = [[1000, 4990], [5000, 8490], [15000, 14990], [30000, 24990]];
const KATSAYI = { 34: 100, "06": 115, 35: 120, 65: 145 };

export function bedelHesapla({ gramaj, hedefKod }) {
  const kademe = KADEME.find(([ust]) => gramaj <= ust);
  if (kademe === undefined) return null;
  const katsayi = KATSAYI[hedefKod.slice(0, 2)] ?? 165;
  return { kurusBedel: Math.round((kademe[1] * katsayi) / 100), gunAdedi: katsayi > 130 ? 4 : 2 };
}
```

İkincisi kilogram ve lira ile çalışır, posta kodu yerine bölge adı bekler ve taşımadığı
gönderide hata atar.

```js
// saglayici/hava.mjs — dis saglayici: kilogram ve lira ile calisir, kapsam disinda hata atar
const BOLGE_TABAN = { yakin: 39.9, orta: 64.5, uzak: 98 };

export function fiyatSor({ kiloAgirlik, bolgeAdi }) {
  const taban = BOLGE_TABAN[bolgeAdi];
  if (taban === undefined) throw new RangeError(`kapsam disi bolge: ${bolgeAdi}`);
  if (kiloAgirlik > 20) throw new RangeError("azami agirlik 20 kg");
  return {
    lira: Math.round((taban + kiloAgirlik * 8.4) * 100) / 100,
    aktarmaGunu: bolgeAdi === "uzak" ? 3 : 1,
  };
}
```

Kitaplığın kendi sözleşmesi tek: bir teklif `{ tasiyici, tutar, gun }` nesnesidir, tutar
kuruştur, taşınamayan gönderi için teklif `null`'dır. Bölge tablosu ve örnek gönderiler
kitaplığın kendi malıdır; iki düzen de bunları paylaşır.

```js
// bolge.mjs — kitapligin kendi bolge tablosu ve ornek gonderiler
const POSTA_BOLGE = { 34: "yakin", "06": "orta", 35: "orta", 65: "uzak" };

export const bolgeBul = (postaKodu) => POSTA_BOLGE[postaKodu.slice(0, 2)] ?? "bilinmeyen";

export const ORNEK = [
  { kod: "G-1", agirlik: 0.8, postaKodu: "34100" },
  { kod: "G-2", agirlik: 12, postaKodu: "06500" },
  { kod: "G-3", agirlik: 26, postaKodu: "65100" },
  { kod: "G-4", agirlik: 3, postaKodu: "48200" },
  { kod: "G-5", agirlik: 8, postaKodu: "35400" },
];
```

## Çevirinin İstemciye Dağıldığı Düzen

İlk düzende çeviriyi sağlayıcıyı çağıran her yer kendisi yapar. Teklifleri sıralayan modül
gram çevirisini, lira çevirisini ve iki ayrı hata biçimini kendi gövdesinde taşır.

```js
// dogrudan/secim.mjs — secim iki saglayicinin sozlugunu ve birimini kendi icinde cevirir
import { bedelHesapla } from "../saglayici/kara.mjs";
import { fiyatSor } from "../saglayici/hava.mjs";
import { bolgeBul } from "../bolge.mjs";

export function teklifler(gonderi) {
  const cikan = [];
  const k = bedelHesapla({ gramaj: Math.round(gonderi.agirlik * 1000), hedefKod: gonderi.postaKodu });
  if (k !== null) cikan.push({ tasiyici: "kara", tutar: k.kurusBedel, gun: k.gunAdedi });
  try {
    const h = fiyatSor({ kiloAgirlik: gonderi.agirlik, bolgeAdi: bolgeBul(gonderi.postaKodu) });
    cikan.push({ tasiyici: "hava", tutar: Math.round(h.lira * 100), gun: h.aktarmaGunu });
  } catch (e) {
    if (!(e instanceof RangeError)) throw e;
  }
  return cikan.sort((a, b) => a.tutar - b.tutar);
}
```

İkinci bir soru — bir gönderiyi kaç taşıyıcının aldığı — aynı çevirinin ikinci kopyasını
doğurur. Kopyalanan şey birim değil, "taşımıyorum" ifadesinin iki sağlayıcıda iki farklı
biçim aldığı bilgisidir.

```js
// dogrudan/kapsam.mjs — ayni cevirinin ikinci kopyasi: kac saglayici tasiyabiliyor
import { bedelHesapla } from "../saglayici/kara.mjs";
import { fiyatSor } from "../saglayici/hava.mjs";
import { bolgeBul } from "../bolge.mjs";

export function tasiyanSayisi(gonderi) {
  let sayi = 0;
  if (bedelHesapla({ gramaj: Math.round(gonderi.agirlik * 1000), hedefKod: gonderi.postaKodu }) !== null) sayi += 1;
  try {
    fiyatSor({ kiloAgirlik: gonderi.agirlik, bolgeAdi: bolgeBul(gonderi.postaKodu) });
    sayi += 1;
  } catch (e) {
    if (!(e instanceof RangeError)) throw e;
  }
  return sayi;
}
```

## Çözüm: Sağlayıcı Başına Bir Bağdaştırıcı

Kalıbın çözümü, her sağlayıcının önüne kitaplığın sözleşmesini karşılayan bir nesne
koymaktır. Bağdaştırıcı üç şeyi çevirir: alan adlarını, birimleri ve hata biçimini.

```js
// bagdastirici/kara-bagdastirici.mjs — gram/kurus sozlugunu kitapligin teklif sozlesmesine cevirir
import { bedelHesapla } from "../saglayici/kara.mjs";

export const karaBagdastirici = {
  teklif(gonderi) {
    const k = bedelHesapla({ gramaj: Math.round(gonderi.agirlik * 1000), hedefKod: gonderi.postaKodu });
    return k === null ? null : { tasiyici: "kara", tutar: k.kurusBedel, gun: k.gunAdedi };
  },
};
```

```js
// bagdastirici/hava-bagdastirici.mjs — lira/bolge sozlugunu ve hatayi ayni sozlesmeye cevirir
import { fiyatSor } from "../saglayici/hava.mjs";
import { bolgeBul } from "../bolge.mjs";

export const havaBagdastirici = {
  teklif(gonderi) {
    try {
      const h = fiyatSor({ kiloAgirlik: gonderi.agirlik, bolgeAdi: bolgeBul(gonderi.postaKodu) });
      return { tasiyici: "hava", tutar: Math.round(h.lira * 100), gun: h.aktarmaGunu };
    } catch (e) {
      if (e instanceof RangeError) return null;
      throw e;
    }
  },
};
```

Sağlayıcıyı çağıran iki modül artık tek sözleşme görür ve sağlayıcı adlarını hiç bilmez.

```js
// bagdastirici/secim.mjs — saglayici sozlugunden hicbir ad gecmez
export const teklifler = (gonderi, bagdastiricilar) =>
  bagdastiricilar
    .map((b) => b.teklif(gonderi))
    .filter((t) => t !== null)
    .sort((a, b) => a.tutar - b.tutar);
```

```js
// bagdastirici/kapsam.mjs — tek sozlesme uzerinden sayar
import { teklifler } from "./secim.mjs";

export const tasiyanSayisi = (gonderi, bagdastiricilar) => teklifler(gonderi, bagdastiricilar).length;
```

```js
// bagdastirici/kur.mjs — bilesim koku: hangi bagdastiricilarin kullanildigini yalniz burasi bilir
import { karaBagdastirici } from "./kara-bagdastirici.mjs";
import { havaBagdastirici } from "./hava-bagdastirici.mjs";

export const BAGDASTIRICILAR = [karaBagdastirici, havaBagdastirici];
```

Karşılaştırmanın anlamı için iki düzenin aynı gönderilere aynı yanıtı vermesi gerekir.

```js
// kosum.mjs — iki duzen ayni teklif sirasini ve ayni tasiyan sayisini veriyor mu
import { ORNEK } from "./bolge.mjs";
import { teklifler as dogrudanTeklif } from "./dogrudan/secim.mjs";
import { tasiyanSayisi as dogrudanSayi } from "./dogrudan/kapsam.mjs";
import { teklifler as kopruluTeklif } from "./bagdastirici/secim.mjs";
import { tasiyanSayisi as kopruluSayi } from "./bagdastirici/kapsam.mjs";
import { BAGDASTIRICILAR } from "./bagdastirici/kur.mjs";

let ayrilan = 0;
for (const g of ORNEK) {
  const a = `${JSON.stringify(dogrudanTeklif(g))} tasiyan=${dogrudanSayi(g)}`;
  const b = `${JSON.stringify(kopruluTeklif(g, BAGDASTIRICILAR))} tasiyan=${kopruluSayi(g, BAGDASTIRICILAR)}`;
  if (a !== b) ayrilan += 1;
  console.log(`${g.kod} ${a}`);
}
console.log(`ayrilan sonuc = ${ayrilan} / ${ORNEK.length}`);
```

```
G-1 [{"tasiyici":"hava","tutar":4662,"gun":1},{"tasiyici":"kara","tutar":4990,"gun":2}] tasiyan=2
G-2 [{"tasiyici":"hava","tutar":16530,"gun":1},{"tasiyici":"kara","tutar":17239,"gun":2}] tasiyan=2
G-3 [{"tasiyici":"kara","tutar":36236,"gun":4}] tasiyan=1
G-4 [{"tasiyici":"kara","tutar":14009,"gun":4}] tasiyan=1
G-5 [{"tasiyici":"hava","tutar":13170,"gun":1},{"tasiyici":"kara","tutar":17988,"gun":2}] tasiyan=2
ayrilan sonuc = 0 / 5
```

Beş gönderide aynı sıra, aynı tutar, aynı taşıyan sayısı. Kademe dışı ağırlık (G-3) ve
tablosuz posta kodu (G-4) dâhil, iki düzen davranışta ayrılmıyor.

## Bağlaşımın Ölçülmesi

Ölçü, Tasarım İlkeleri kursundaki Çerçeve Kodunu Uzakta Tutmak dersinin kullandığı ölçünün
aynısıdır: dış sözlükten en az bir ad geçen dosya sayısı. Yanına ikinci bir sütun konuyor —
birim çevirisinin kaç yerde tekrarlandığı.

```js
// sozluk-say.mjs — saglayici sozlugunden ad gecen dosyalari ve birim cevirisini sayar
import { readdirSync, readFileSync } from "node:fs";

const SOZLUK = ["bedelHesapla", "gramaj", "hedefKod", "kurusBedel", "gunAdedi",
  "fiyatSor", "kiloAgirlik", "bolgeAdi", "lira", "aktarmaGunu"];
const CEVRIM = [/\* 1000/g, /lira \* 100/g];

for (const dizin of ["dogrudan", "bagdastirici"]) {
  const dosyalar = readdirSync(dizin).filter((d) => d.endsWith(".mjs")).sort();
  let dokunan = 0;
  console.log(`${dizin}/`);
  for (const d of dosyalar) {
    const metin = readFileSync(`${dizin}/${d}`, "utf8");
    const ad = SOZLUK.filter((a) => new RegExp(`\\b${a}\\b`).test(metin)).length;
    const cevrim = CEVRIM.reduce((t, k) => t + (metin.match(k)?.length ?? 0), 0);
    if (ad > 0) dokunan += 1;
    console.log(`  ${d.padEnd(24)} saglayici adi=${String(ad).padStart(2)}  birim cevirisi=${cevrim}`);
  }
  console.log(`  sozluge dokunan dosya = ${dokunan} / ${dosyalar.length}`);
}
```

```
dogrudan/
  kapsam.mjs               saglayici adi= 6  birim cevirisi=1
  secim.mjs                saglayici adi=10  birim cevirisi=2
  sozluge dokunan dosya = 2 / 2
bagdastirici/
  hava-bagdastirici.mjs    saglayici adi= 5  birim cevirisi=1
  kapsam.mjs               saglayici adi= 0  birim cevirisi=0
  kara-bagdastirici.mjs    saglayici adi= 5  birim cevirisi=1
  kur.mjs                  saglayici adi= 0  birim cevirisi=0
  secim.mjs                saglayici adi= 0  birim cevirisi=0
  sozluge dokunan dosya = 2 / 5
```

İlk düzende iki dosyanın ikisi de sözlüğe dokunuyor; ikincisinde beş dosyanın ikisi ve
dokunan iki dosya bağdaştırıcıların kendisi. Seçim ile kapsam sayımı sıfır ada dokunuyor.
İkinci sütun bağlaşımın türünü de gösteriyor: birim çevirisi ilk düzende üç yerde, ikincide
iki yerde — ve o iki yer aynı sağlayıcının tek çeviri noktasıdır, aynı çevirinin kopyası
değil.

Tasarım İlkeleri kursundaki o ders bağdaştırıcıyı bir **yalıtım** aracı olarak kullanmıştı:
tek bir çerçevenin bağlam nesnesini kitaplığın düz istek biçimine çeviren tek dosya, denetim
ters yönde. Burada durum farklıdır — sağlayıcı sayısı birden çoktur, denetim kitaplıktadır ve
çevirinin amacı yalıtım değil **aynı sözleşme altında karşılaştırılabilirlik**tir. Beş
gönderinin tekliflerinin tek listede sıralanabilmesi bu ortak sözleşmenin sonucudur.

## Üçüncü Sağlayıcı Eklenince

Sayılar bir değişiklik geldiğinde karşılığını verir. Üçüncü sağlayıcı ilk ikisine de benzemez:
konumsal argüman alır, dizi döndürür ve taşımadığında `undefined` verir. İki ağaç kopyalanıp
değişiklik ikisine de uygulanıyor.

```sh
mkdir -p yeni && cp -r saglayici bolge.mjs dogrudan bagdastirici kosum.mjs yeni/
ls yeni
```

```
bagdastirici
bolge.mjs
dogrudan
kosum.mjs
saglayici
```

```js
// yeni/saglayici/deniz.mjs — ucuncu dis saglayici: konumsal arguman alir, dizi doner, tasimadiginda undefined
const LIMAN = { 34: 1, 35: 1.1 };

export function navlun(agirlikKg, postaKodu) {
  const carpan = LIMAN[postaKodu.slice(0, 2)];
  if (carpan === undefined) return undefined;
  return [Math.round((2600 + agirlikKg * 260) * carpan), 6];
}
```

```js
// yeni/bagdastirici/deniz-bagdastirici.mjs — konumsal arguman ve dizi yanitini ayni sozlesmeye cevirir
import { navlun } from "../saglayici/deniz.mjs";

export const denizBagdastirici = {
  teklif(gonderi) {
    const d = navlun(gonderi.agirlik, gonderi.postaKodu);
    return d === undefined ? null : { tasiyici: "deniz", tutar: d[0], gun: d[1] };
  },
};
```

Bağdaştırıcılı düzende yapılacak tek düzenleme bileşim kökündeki listedir. Doğrudan düzende
ise iki dosyanın gövdesine üçüncü hata biçimi girer. Aşağıdaki betik iki düzenlemeyi de
uygular, iki ağacı koşturur ve farkı sayar; yerinde düzenlemede yedek uzantısı verildiği için
GNU ve BSD `sed` aynı davranır.

```sh
cd yeni
sed -i.y -e 's#^import { bolgeBul } from "../bolge.mjs";#&\nimport { navlun } from "../saglayici/deniz.mjs";#' dogrudan/secim.mjs dogrudan/kapsam.mjs
sed -i.y 's#^  return cikan.sort#  const d = navlun(gonderi.agirlik, gonderi.postaKodu);\n  if (d !== undefined) cikan.push({ tasiyici: "deniz", tutar: d[0], gun: d[1] });\n&#' dogrudan/secim.mjs
sed -i.y 's#^  return sayi;#  if (navlun(gonderi.agirlik, gonderi.postaKodu) !== undefined) sayi += 1;\n&#' dogrudan/kapsam.mjs
sed -i.y -e 's#^import { havaBagdastirici } from "./hava-bagdastirici.mjs";#&\nimport { denizBagdastirici } from "./deniz-bagdastirici.mjs";#' \
         -e 's#havaBagdastirici\];#havaBagdastirici, denizBagdastirici];#' bagdastirici/kur.mjs
rm -f dogrudan/*.y bagdastirici/*.y
node kosum.mjs | head -2
for d in dogrudan bagdastirici; do
  echo "$d"
  echo "  duzenlenen var olan dosya = $(diff -rq "../$d" "$d" | grep -c '^Files ')"
  echo "  yeni dosya                = $(diff -rq "../$d" "$d" | grep -c '^Only in ')"
  echo "  eklenen satir             = $(diff -rN "../$d" "$d" | grep '^>' | grep -cvE '^> *(//|$)')"
done
```

```
G-1 [{"tasiyici":"deniz","tutar":2808,"gun":6},{"tasiyici":"hava","tutar":4662,"gun":1},{"tasiyici":"kara","tutar":4990,"gun":2}] tasiyan=3
G-2 [{"tasiyici":"hava","tutar":16530,"gun":1},{"tasiyici":"kara","tutar":17239,"gun":2}] tasiyan=2
dogrudan
  duzenlenen var olan dosya = 2
  yeni dosya                = 0
  eklenen satir             = 5
bagdastirici
  duzenlenen var olan dosya = 1
  yeni dosya                = 1
  eklenen satir             = 9
```

Doğrudan düzende iki var olan dosya düzenlendi, bağdaştırıcılı düzende bir — ve o bir dosya
bileşim köküdür, iş kuralı taşımaz. Bağdaştırıcılı düzen daha çok satır ekledi (9'a 5),
çünkü yeni sağlayıcının çevirisi kendi dosyasında tam bir nesne olarak duruyor. Açık–Kapalı
İlkesi dersindeki ayrım burada da geçerlidir: eklenen satır yeniden doğrulanmayı gerektirmez,
düzenlenen satır gerektirir. Doğrudan düzende düzenlenen iki gövde, daha önce doğru çalışan
iki sağlayıcının hata yollarını da içeriyordu.

## Bedel ve Ne Zaman Uygulanmayacağı

Kalıbın bedeli aynı ölçekte sayılabilir.

```sh
for d in dogrudan bagdastirici; do
  echo "$d: $(ls $d/*.mjs | wc -l | tr -d ' ') dosya, $(cat $d/*.mjs | grep -cvE '^ *(//)?$') satir"
done
```

```
dogrudan: 2 dosya, 31 satir
bagdastirici: 5 dosya, 35 satir
```

Üç dosya ve dört satır fazladan; buna bir de dolaylılık basamağı eklenir. Doğrudan düzende
bir teklifin izlenmesi için iki dosya okunur (seçim ve sağlayıcı), bağdaştırıcılı düzende üç
(seçim, bağdaştırıcı, sağlayıcı). Bağdaştırıcı sağlayıcı listesi enjekte edildiği için ithal
grafiğinde de görünmez: `bagdastirici/secim.mjs` hiçbir sağlayıcıyı ithal etmez, bağ çalışma
zamanında bileşim kökünde kurulur. Hata ayıklarken izlenecek çağrı sayısı artar.

Kalıp üç durumda karşılığını vermez. Sağlayıcı **tek** ve sözleşmesi kitaplığın denetimindeyse
çevirinin ayrı dosyaya çıkarılması yalnız bir dosya ekler. Sağlayıcının sözlüğü kitaplığın
sözlüğüyle zaten örtüşüyorsa bağdaştırıcı gövdesi alan adlarını birebir kopyalayan bir katmana
düşer. Üçüncüsü daha inceden ısırır: bağdaştırıcı yalnız eşlediği kadarını geçirir. Bir
sağlayıcının sunduğu ama sözleşmede karşılığı olmayan yetenek — parçalı gönderi, teslim
saati aralığı — iş kuralı tarafından kullanılamaz; kullanılacaksa önce ortak sözleşmenin
büyütülmesi ve bütün bağdaştırıcıların o alana bir yanıt vermesi gerekir. Ortak sözleşmenin
maliyeti sağlayıcı sayısıyla doğru orantılı büyür.

## Özet

- Bağdaştırıcının çözdüğü problem, kitaplığın belirlemediği bir arayüzle kitaplığın kendi
  sözleşmesi arasındaki uyumsuzluktur; çözüm, çeviriyi sağlayıcı başına tek bir nesnede
  toplamaktır.
- Çeviri üç şeyi kapsar: alan adları, birimler ve "taşımıyorum" ifadesinin biçimi — sonuncusu
  `null` ile hata arasındaki fark olduğu için en çok kopyalanan bilgidir.
- Dış sözlüğe dokunan dosya sayısı doğrudan düzende 2/2, bağdaştırıcılı düzende 2/5'tir ve
  dokunan iki dosya bağdaştırıcıların kendisidir; birim çevirisi 3 yerden 2 yere iner.
- Üçüncü sağlayıcı eklenince doğrudan düzende 2 var olan dosya düzenlendi, bağdaştırıcılı
  düzende 1 (bileşim kökü) düzenlendi ve 1 yeni dosya eklendi; beş gönderinin yanıtları iki
  düzende de aynı kaldı.
- Bedel 3 dosya, 4 satır ve bir dolaylılık basamağıdır; tek sağlayıcılı, sözlüğü örtüşen ya
  da yeteneklerinin ortak sözleşmeye sığmadığı durumlarda bu bedel karşılıksız kalır.

## Sonraki Adım

Bağdaştırıcı var olan bir arayüzü sonradan uydurdu: sağlayıcılar zaten yazılmıştı, kitaplığın
sözleşmesi de. Kalıp ikisinin arasına girdi. Bir sonraki problem baştan tasarım aşamasında
çıkar. Kitaplık ücret raporu üretecekse iki bağımsız eksen vardır: raporun **türü** (özet,
kademe dökümü, bölge dökümü) ve raporun **biçimi** (düz metin, ayrılmış değer, hizalanmış
tablo). İki eksen kalıtımla birleştirilirse tür sayısı ile biçim sayısının çarpımı kadar sınıf
doğar. Sonraki ders bu çarpımı üç tür ve iki biçim için sayar, iki ekseni ayrı hiyerarşilere
bölen köprü kalıbını yazar ve üçüncü bir biçim eklendiğinde iki düzende eklenen tip ile
eklenen satırı karşılaştırır.
