---
title: 'Sorumluluk Zinciri'
source: 'https://academia.sh/tr/kurslar/tasarim-kaliplari/sorumluluk-zinciri'
course: 'Tasarım Kalıpları'
language: tr
updated: '2026-08-17T18:10:37+00:00'
license: 'CC BY-SA 4.0'
---

# Sorumluluk Zinciri

İndirim kurallarının tek gövdede sıralanması ile isteğin işleyiciler arasında aktarılmasının karşılaştırılması: kural sırası değiştiğinde değişen satır sayısı, üç yeni kural eklendiğinde en karmaşık gövdenin çevrimsel karmaşıklığı ile dosya sayısının nasıl büyüdüğü ve kalıbın bedeli olarak hiçbir kurala uymayan isteğin ayırt edilemeyişi.

Durum nesnesi bir soruyu tek bir yerden yanıtlıyordu: bu olay burada geçerli mi. Ücret
düzeltmeleri tarafında soru tek bir yere sorulamıyor. Bir gönderiye sözleşmeli müşteri indirimi,
hacim indirimi, kampanya indirimi ve elle girilen düzeltme sırayla uygulanabilir; kuralların bir
kısmı gönderiye uymaz ve atlanır, bir kısmı uygulandıktan sonra zinciri keser. Bugün bu kurallar
tek gövdede iç içe koşullarla duruyor ve sıraları gövdenin akışına gömülü.

**Sorumluluk zinciri** (chain of responsibility) isteği bir işleyici dizisine verir. Her işleyici
isteğe uygun olup olmadığına kendisi karar verir, uygunsa işini yapar, ardından isteği sonraki
işleyiciye bırakır ya da zinciri keser. Ölçülecek sayılar kural sırası değiştiğinde değişen satır
sayısı, kural sayısı arttığında en karmaşık gövdenin çevrimsel karmaşıklığı ve dosya sayısıdır.

## Dört Kural

Kurallar sırayla şunlar: sözleşmeli müşteriye yüzde on iki, yirmi kilogramdan ağır gönderiye
yüzde sekiz, üçüncü bölgedeki hafif gönderiye yüzde on beş (uygulanınca zinciri keser), elle
girilen orana eşit düzeltme (uygulanınca zinciri keser).

```sh
mkdir -p gomulu zincir

cat > zincir/sozlesmeli.mjs <<'SON'
// zincir/sozlesmeli.mjs
export const sozlesmeli = {
  ad: "sozlesmeli",
  keser: false,
  uygunMu: (g) => g.sozlesmeli === true,
  oran: () => 12,
};
SON

cat > zincir/hacim.mjs <<'SON'
// zincir/hacim.mjs
export const hacim = {
  ad: "hacim",
  keser: false,
  uygunMu: (g) => g.agirlik >= 20,
  oran: () => 8,
};
SON

cat > zincir/kampanya.mjs <<'SON'
// zincir/kampanya.mjs — uygulandiginda zinciri keser
export const kampanya = {
  ad: "kampanya",
  keser: true,
  uygunMu: (g) => g.bolge === "3" && g.agirlik < 5,
  oran: () => 15,
};
SON

cat > zincir/elle.mjs <<'SON'
// zincir/elle.mjs — uygulandiginda zinciri keser
export const elle = {
  ad: "elle",
  keser: true,
  uygunMu: (g) => typeof g.elleOran === "number",
  oran: (g) => g.elleOran,
};
SON
```

```js
// veri.mjs — indirim kurallarinin denenecegi gonderiler
export const GONDERILER = [
  { kod: "GN-1", agirlik: 2, bolge: "1", sozlesmeli: false },
  { kod: "GN-2", agirlik: 6, bolge: "2", sozlesmeli: true },
  { kod: "GN-3", agirlik: 22, bolge: "3", sozlesmeli: false },
  { kod: "GN-4", agirlik: 3, bolge: "3", sozlesmeli: true },
  { kod: "GN-5", agirlik: 25, bolge: "2", sozlesmeli: false, elleOran: 5 },
];
```

Birinci sürümde dört kural ve sıraları tek gövdededir. Gövde ayrıca kaç kuralın yoklandığını
sayar; bu sayı iki sürümü karşılaştırmayı sağlar.

```js
// gomulu/indirim.mjs — dort kural ve siralari tek govdede
export function indirim(gonderi) {
  const uygulanan = [];
  let oran = 0;
  let ziyaret = 1;
  if (gonderi.sozlesmeli === true) {
    oran += 12;
    uygulanan.push("sozlesmeli");
  }
  ziyaret += 1;
  if (gonderi.agirlik >= 20) {
    oran += 8;
    uygulanan.push("hacim");
  }
  ziyaret += 1;
  if (gonderi.bolge === "3" && gonderi.agirlik < 5) {
    oran += 15;
    uygulanan.push("kampanya");
    return { oran, ziyaret, uygulanan };
  }
  ziyaret += 1;
  if (typeof gonderi.elleOran === "number") {
    oran += gonderi.elleOran;
    uygulanan.push("elle");
    return { oran, ziyaret, uygulanan };
  }
  return { oran, ziyaret, uygulanan };
}
```

İkinci sürümde sıra bir dizidir ve tek satırda durur.

```js
// zincir/sira.mjs — zincirin sirasi tek satirda
import { sozlesmeli } from "./sozlesmeli.mjs";
import { hacim } from "./hacim.mjs";
import { kampanya } from "./kampanya.mjs";
import { elle } from "./elle.mjs";

export const SIRA = [sozlesmeli, hacim, kampanya, elle];
```

```js
// zincir/zincir.mjs — istek kesilene ya da zincir bitene kadar aktarilir
export function indirim(kurallar, gonderi) {
  const uygulanan = [];
  let oran = 0;
  let ziyaret = 0;
  for (const kural of kurallar) {
    ziyaret += 1;
    if (kural.uygunMu(gonderi) === false) continue;
    oran += kural.oran(gonderi);
    uygulanan.push(kural.ad);
    if (kural.keser) break;
  }
  return { oran, ziyaret, uygulanan };
}
```

Zincir yürütücüsü hiçbir kuralın ne yaptığını bilmez; yalnızca `uygunMu`, `oran` ve `keser`
üçlüsünü kullanır. Kural sayısı ne olursa olsun bu gövde değişmez.

## Davranışın Eşitliği

```js
// kosum.mjs — iki surumu ayni gonderilerle kosar ve sapmayi sayar
import { GONDERILER } from "./veri.mjs";
import { indirim as gomuluIndirim } from "./gomulu/indirim.mjs";
import { indirim as zincirIndirim } from "./zincir/zincir.mjs";
import { SIRA } from "./zincir/sira.mjs";

let sapma = 0;
for (const g of GONDERILER) {
  const a = gomuluIndirim(g);
  const b = zincirIndirim(SIRA, g);
  if (a.oran !== b.oran || a.uygulanan.join() !== b.uygulanan.join()) sapma += 1;
  console.log(
    `${g.kod} gomulu oran=${a.oran} ziyaret=${a.ziyaret} [${a.uygulanan.join(" ")}]  zincir oran=${b.oran} ziyaret=${b.ziyaret} [${b.uygulanan.join(" ")}]`,
  );
}
console.log(`iki surum arasindaki sapma sayisi = ${sapma}`);
```

```
GN-1 gomulu oran=0 ziyaret=4 []  zincir oran=0 ziyaret=4 []
GN-2 gomulu oran=12 ziyaret=4 [sozlesmeli]  zincir oran=12 ziyaret=4 [sozlesmeli]
GN-3 gomulu oran=8 ziyaret=4 [hacim]  zincir oran=8 ziyaret=4 [hacim]
GN-4 gomulu oran=27 ziyaret=3 [sozlesmeli kampanya]  zincir oran=27 ziyaret=3 [sozlesmeli kampanya]
GN-5 gomulu oran=13 ziyaret=4 [hacim elle]  zincir oran=13 ziyaret=4 [hacim elle]
iki surum arasindaki sapma sayisi = 0
```

Sapma sıfır: oranlar, uygulanan kural listeleri ve ziyaret sayıları birebir aynı. GN-4'te ziyaret
sayısının üç olması kesme davranışının çalıştığını gösteriyor; kampanya uygulandıktan sonra elle
düzeltme yoklanmadı.

## Sıra Değiştiğinde

Elle girilen düzeltmenin her şeyin önüne geçmesi isteniyor: operatörün girdiği oran diğer
indirimleri bastırmalı. Gömülü sürümde bu, bir bloğun gövde içinde taşınmasıdır; zincir sürümünde
dizinin sırasıdır.

```sh
cp -r gomulu gomulu-yeni
cp -r zincir zincir-yeni

cat > gomulu-yeni/indirim.mjs <<'SON'
// gomulu/indirim.mjs — dort kural ve siralari tek govdede
export function indirim(gonderi) {
  const uygulanan = [];
  let oran = 0;
  let ziyaret = 1;
  if (typeof gonderi.elleOran === "number") {
    oran += gonderi.elleOran;
    uygulanan.push("elle");
    return { oran, ziyaret, uygulanan };
  }
  ziyaret += 1;
  if (gonderi.sozlesmeli === true) {
    oran += 12;
    uygulanan.push("sozlesmeli");
  }
  ziyaret += 1;
  if (gonderi.agirlik >= 20) {
    oran += 8;
    uygulanan.push("hacim");
  }
  ziyaret += 1;
  if (gonderi.bolge === "3" && gonderi.agirlik < 5) {
    oran += 15;
    uygulanan.push("kampanya");
    return { oran, ziyaret, uygulanan };
  }
  return { oran, ziyaret, uygulanan };
}
SON

sed -i.y 's/\[sozlesmeli, hacim, kampanya, elle\]/[elle, sozlesmeli, hacim, kampanya]/' zincir-yeni/sira.mjs
rm -f zincir-yeni/*.y

for k in gomulu zincir; do
  echo "$k: duzenlenen dosya=$(diff -rq $k $k-yeni | grep -c '^Files')  degisen satir=$(diff -r -u $k $k-yeni | grep '^[+-][^+-]' | grep -vc '^[+-]//')"
done

cat > kosum-yeni.mjs <<'SON'
import { GONDERILER } from "./veri.mjs";
import { indirim as gomuluIndirim } from "./gomulu-yeni/indirim.mjs";
import { indirim as zincirIndirim } from "./zincir-yeni/zincir.mjs";
import { SIRA } from "./zincir-yeni/sira.mjs";

let sapma = 0;
for (const g of GONDERILER) {
  const a = gomuluIndirim(g);
  const b = zincirIndirim(SIRA, g);
  if (a.oran !== b.oran || a.uygulanan.join() !== b.uygulanan.join()) sapma += 1;
}
const g5 = GONDERILER[4];
console.log(`GN-5 yeni sirada: gomulu oran=${gomuluIndirim(g5).oran}  zincir oran=${zincirIndirim(SIRA, g5).oran}`);
console.log(`yeni sirada iki surum arasindaki sapma sayisi = ${sapma}`);
SON
node kosum-yeni.mjs
```

```
gomulu: duzenlenen dosya=1  degisen satir=12
zincir: duzenlenen dosya=1  degisen satir=2
GN-5 yeni sirada: gomulu oran=5  zincir oran=5
yeni sirada iki surum arasindaki sapma sayisi = 0
```

On iki satıra karşı iki satır. GN-5'in oranı 13'ten 5'e düştü: elle düzeltme öne geçtiği için
hacim indirimi hiç yoklanmadı. Bu, sıranın bir biçim tercihi değil davranışın parçası olduğunu
gösteriyor — ve bu yüzden sıranın nerede yazıldığı önemlidir. Gömülü sürümde sıra, koşulların
gövde içindeki yerleşimidir; okumak için gövdenin tamamını izlemek gerekir. Zincir sürümünde sıra
tek satırlık bir dizidir.

## Kural Sayısı Büyüdüğünde

Kalıbın karşılığını verip vermediği, kural sayısı arttığında görülür. Üç kural daha ekleniyor:
on kilogramdan ağır gönderiye yüzde üç yakıt indirimi, ilk gönderisini yapan müşteriye yüzde on,
üçüncü bölgeye yüzde dört.

```js
// buyume.mjs — uc yeni kural eklenince govde karmasikligi nasil buyuyor
import { cpSync, readFileSync, readdirSync, writeFileSync } from "node:fs";

const KARAR = /\bif\b|&&|\|\||\?|\bcase\b|\bwhile\b|\bfor\b|\bcontinue\b|\bbreak\b/g;
const enKarmasik = (dizin) =>
  Math.max(
    ...readdirSync(dizin).map((d) => (readFileSync(`${dizin}/${d}`, "utf8").replace(/^\/\/.*$/gm, "").match(KARAR) ?? []).length + 1),
  );

const once = { gomulu: enKarmasik("gomulu"), zincir: enKarmasik("zincir") };

cpSync("gomulu", "gomulu-uc", { recursive: true });
cpSync("zincir", "zincir-uc", { recursive: true });

const EK = `  ziyaret += 1;
  if (gonderi.agirlik >= 10) {
    oran += 3;
    uygulanan.push("yakit");
  }
  ziyaret += 1;
  if (gonderi.ilkMi === true) {
    oran += 10;
    uygulanan.push("ilkGonderi");
  }
  ziyaret += 1;
  if (gonderi.bolge === "3") {
    oran += 4;
    uygulanan.push("uzakBolge");
  }
  ziyaret += 1;`;
writeFileSync(
  "gomulu-uc/indirim.mjs",
  readFileSync("gomulu/indirim.mjs", "utf8").replace('  ziyaret += 1;\n  if (gonderi.bolge === "3" && gonderi.agirlik < 5) {', `${EK}\n  if (gonderi.bolge === "3" && gonderi.agirlik < 5) {`),
);

const YENI = [
  ["yakit", "(g) => g.agirlik >= 10", "() => 3"],
  ["ilk-gonderi", "(g) => g.ilkMi === true", "() => 10"],
  ["uzak-bolge", '(g) => g.bolge === "3"', "() => 4"],
];
for (const [ad, kosul, oran] of YENI) {
  const degisken = ad.replace(/-(\w)/g, (_, c) => c.toUpperCase());
  writeFileSync(
    `zincir-uc/${ad}.mjs`,
    `// zincir/${ad}.mjs\nexport const ${degisken} = {\n  ad: "${degisken}",\n  keser: false,\n  uygunMu: ${kosul},\n  oran: ${oran},\n};\n`,
  );
}
writeFileSync(
  "zincir-uc/sira.mjs",
  `${readFileSync("zincir/sira.mjs", "utf8")
    .replace('import { elle } from "./elle.mjs";', 'import { elle } from "./elle.mjs";\nimport { yakit } from "./yakit.mjs";\nimport { ilkGonderi } from "./ilk-gonderi.mjs";\nimport { uzakBolge } from "./uzak-bolge.mjs";')
    .replace("[sozlesmeli, hacim, kampanya, elle]", "[sozlesmeli, hacim, yakit, ilkGonderi, uzakBolge, kampanya, elle]")}`,
);

const sonra = { gomulu: enKarmasik("gomulu-uc"), zincir: enKarmasik("zincir-uc") };
console.log(`en karmasik govde  gomulu: ${once.gomulu} -> ${sonra.gomulu}   zincir: ${once.zincir} -> ${sonra.zincir}`);
console.log(`dosya sayisi       gomulu: ${readdirSync("gomulu").length} -> ${readdirSync("gomulu-uc").length}   zincir: ${readdirSync("zincir").length} -> ${readdirSync("zincir-uc").length}`);

const { GONDERILER } = await import("./veri.mjs");
const g = await import("./gomulu-uc/indirim.mjs");
const z = await import("./zincir-uc/zincir.mjs");
const s = await import("./zincir-uc/sira.mjs");
let sapma = 0;
let dusenIstek = 0;
for (const gonderi of GONDERILER) {
  const a = g.indirim(gonderi);
  const b = z.indirim(s.SIRA, gonderi);
  if (a.oran !== b.oran) sapma += 1;
  if (b.uygulanan.length === 0) dusenIstek += 1;
}
console.log(`yedi kuralda sapma=${sapma}  hicbir kurala uymayan gonderi=${dusenIstek}`);
```

```
en karmasik govde  gomulu: 6 -> 9   zincir: 6 -> 6
dosya sayisi       gomulu: 1 -> 1   zincir: 6 -> 9
yedi kuralda sapma=0  hicbir kurala uymayan gonderi=1
```

Dört kuralda kalıp karmaşıklık kazancı vermiyordu: altıya karşı altı. Zincir yürütücüsünün
kendisi, dört kuralı sıralayan gövde kadar karmaşıktır. Yedi kuralda ise yollar ayrılıyor.
Gömülü gövde 6'dan 9'a çıkıyor, zincir yürütücüsü 6'da kalıyor çünkü her yeni kural gövdeye
değil kendi dosyasına yazılıyor. Bedelin karşılığı da aynı satırda: dosya sayısı 6'dan 9'a
çıkıyor, gömülü sürümde 1'de kalıyor.

Kalıbın eşiği bu iki eğrinin kesiştiği yerdir ve buradaki ölçüm eşiği dört kural civarına
koyuyor: dördün altında zincir yalnızca dosya ekler.

## Bedelin İkinci Kalemi

Son satırın son sayısı ayrı bir bedeldir: bir gönderi hiçbir kurala uymadı. Zincir bittiğinde
oran sıfırdır, ama "indirim yok" ile "hiçbir kural isteği ele almadı" aynı sonuçla temsil edilir.
Gömülü gövdede de aynı belirsizlik var, fakat zincirde bu sessizlik daha kolay gözden kaçar:
işleyicilerden biri yanlışlıkla listeden çıkarıldığında sonuç hâlâ geçerli görünür, yalnızca
oran düşer. Zincirin sonuna hiçbir zaman uygulanmayan bir işleyici koymak bu belirsizliği
kapatmanın bilinen yoludur; bu, konunun dokuzuncu dersinde ölçülecek boş nesne kalıbıdır.

## Özet

- Sorumluluk zinciri isteği bir işleyici dizisine verir; her işleyici uygunluğuna kendi karar
  verir, işini yapar ve isteği sonrakine bırakır ya da zinciri keser.
- Dört kuralla iki sürüm birebir aynı oran, aynı uygulanan kural listesi ve aynı ziyaret
  sayısını üretti; sapma 0 çıktı.
- Kural sırası değiştiğinde gömülü sürümde 12, zincir sürümünde 2 satır değişti; sıra davranışın
  parçasıdır — elle düzeltme öne geçince GN-5'in oranı 13'ten 5'e indi.
- Dört kuralda en karmaşık gövde ikisinde de 6; yedi kuralda gömülü sürüm 9'a çıktı, zincir 6'da
  kaldı, karşılığında dosya sayısı 6'dan 9'a yükseldi.
- Bedel: kural başına bir dosya, ve hiçbir işleyicinin ele almadığı istek ile indirimsiz istek
  aynı sonuçla temsil edilir.

## Sonraki Adım

Zincir isteği bir dizide sırayla dolaştırıyor: dolaşma düz, tek yönlü ve sıra dizinin kendisinde.
Kitaplığın rota tarafında dolaşılacak yapı düz değil. Bir rota, aktarma noktalarından oluşan bir
ağaçtır: merkez, bölge deposu, dağıtım şubesi, teslim noktası. Bu ağaç üzerinde birbirinden
bağımsız beş işlem yapılıyor — toplam mesafe, en uzun bekleme, kapasite denetimi, etiket
listesi, maliyet toplamı — ve her işlem ağacı kendi gezinme koduyla dolaşıyor. Sonraki ders
gezinme kodunun kaç kez yinelendiğini sayar, sonra gezinmeyi işlemden ayırıp yeni işlem ve yeni
düğüm türü eklendiğinde dokunulan dosya sayısını iki yönde birden ölçer.
