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

# Durum

Teslimat yaşam döngüsünün beş bayrakla ve iç içe koşullarla tutulması ile her durumun kendi geçişlerini bilen nesnelere çevrilmesinin karşılaştırılması: otuz durum-olay çiftinde kabul edilen geçersiz geçiş sayısı, çevrimsel karmaşıklık, temsil edilebilir birleşim sayısı, yeni durum eklendiğinde değişen satır sayısı ve kalıbın bedeli olarak geçiş hedefi hatasının gecikmeli görünmesi.

Şablonda sıra sabitti; hangi adımın çalışacağı baştan belliydi. Teslimatın kendisinde ise
hangi işlemin geçerli olduğu o anki duruma bağlıdır. Bir teslimat oluşturulmuş, toplanmış,
yolda, dağıtımda, teslim edilmiş ya da iade edilmiş olabilir; toplanmamış bir gönderi dağıtıma
çıkamaz, teslim edilmiş bir gönderi iade edilemez. Bugün bu kurallar beş bayrakla ve iç içe
koşullarla denetleniyor.

Durum makinesi kavramı Yazılım Kalitesi ve Test müfredatında bir kez kurulmuştu: hata yaşam
döngüsü orada durum ve geçiş tablosuyla anlatılmış, geçişler test durumu üretmek için
kullanılmıştı. Burada yeni olan makinenin kendisi değil, **nesnelerle ifade edilişi**. **Durum**
(state) kalıbı her durumu bir nesne yapar ve o nesneye yalnızca kendi izin verdiği geçişleri
bildirir. Ölçülecek sayılar kabul edilen geçersiz geçiş sayısı, çevrimsel karmaşıklık ve
temsil edilebilir bayrak birleşimi sayısıdır.

## Bayraklarla Tutulan Yaşam Döngüsü

Beş bayrak ve beş olay var. Her olay kendi koşulunu kendisi kurar; durumun adı bayrakların
sıralı denetiminden türetilir.

```sh
mkdir -p bayrak durum
```

```js
// bayrak/teslimat.mjs — durum bes bayrakta, gecisler ic ice kosullarda
export const teslimat = (kod) => {
  const d = { toplandi: false, yolda: false, dagitimda: false, teslim: false, iade: false };
  const durumAdi = () =>
    d.iade ? "iade" : d.teslim ? "teslim" : d.dagitimda ? "dagitimda" : d.yolda ? "yolda" : d.toplandi ? "toplandi" : "olusturuldu";
  return {
    kod,
    durumAdi,
    olay(ad) {
      if (ad === "topla") {
        if (d.toplandi === false && d.teslim === false) {
          d.toplandi = true;
          return true;
        }
        return false;
      }
      if (ad === "yolaCik") {
        if (d.toplandi && d.yolda === false) {
          d.yolda = true;
          return true;
        }
        return false;
      }
      if (ad === "dagitimaVer") {
        if (d.yolda && d.dagitimda === false) {
          d.dagitimda = true;
          return true;
        }
        return false;
      }
      if (ad === "teslimEt") {
        if (d.yolda && d.teslim === false && d.iade === false) {
          d.teslim = true;
          return true;
        }
        return false;
      }
      if (ad === "iadeEt") {
        if (d.yolda && d.iade === false) {
          d.iade = true;
          return true;
        }
        return false;
      }
      return false;
    },
  };
};
```

## Durumun Nesneleştirilmesi

İkinci sürümde her durum bir nesnedir ve kendi geçiş kümesini taşır. Geçiş kümesi olay adından
sonraki durumun adına giden bir eşlemedir.

```js
// durum/durumlar.mjs — her durum yalnizca kendi gecislerini bilir
const durum = (ad, gecisler) => ({
  ad,
  izin: (olay) => Object.hasOwn(gecisler, olay),
  sonraki: (olay) => gecisler[olay],
});

export const DURUMLAR = {
  olusturuldu: durum("olusturuldu", { topla: "toplandi" }),
  toplandi: durum("toplandi", { yolaCik: "yolda" }),
  yolda: durum("yolda", { dagitimaVer: "dagitimda", iadeEt: "iade" }),
  dagitimda: durum("dagitimda", { teslimEt: "teslim", iadeEt: "iade" }),
  teslim: durum("teslim", {}),
  iade: durum("iade", {}),
};
```

```js
// durum/teslimat.mjs — gecis karari durum nesnesine sorulur
import { DURUMLAR } from "./durumlar.mjs";

export const teslimat = (kod) => {
  let su = DURUMLAR.olusturuldu;
  return {
    kod,
    durumAdi: () => su.ad,
    olay(ad) {
      if (su.izin(ad) === false) return false;
      su = DURUMLAR[su.sonraki(ad)];
      return true;
    },
  };
};
```

Teslimat nesnesi artık hangi olayın hangi durumda geçerli olduğunu bilmiyor; kararı elindeki
durum nesnesine soruyor. Bu, sorma–söyle ilkesinin durum makinesindeki karşılığıdır: bilgi
kararın verildiği yerdedir.

## Otuz Çiftin Tek Tek Denenmesi

Ölçüm, altı durumun her birinde beş olayın da denenmesiyle yapılır: otuz çift. Beklenti tablosu
ve her duruma götüren yol ayrı bir dosyada durur.

```js
// tablo.mjs — gecerli gecis tablosu ve her duruma goturen yol
export const OLAYLAR = ["topla", "yolaCik", "dagitimaVer", "teslimEt", "iadeEt"];
export const YOL = {
  olusturuldu: [],
  toplandi: ["topla"],
  yolda: ["topla", "yolaCik"],
  dagitimda: ["topla", "yolaCik", "dagitimaVer"],
  teslim: ["topla", "yolaCik", "dagitimaVer", "teslimEt"],
  iade: ["topla", "yolaCik", "iadeEt"],
};
export const GECERLI = {
  olusturuldu: { topla: "toplandi" },
  toplandi: { yolaCik: "yolda" },
  yolda: { dagitimaVer: "dagitimda", iadeEt: "iade" },
  dagitimda: { teslimEt: "teslim", iadeEt: "iade" },
  teslim: {},
  iade: {},
};
```

```js
// kosum.mjs — otuz durum-olay ciftini iki surumde de dener
import { teslimat as bayrakTeslimat } from "./bayrak/teslimat.mjs";
import { teslimat as durumTeslimat } from "./durum/teslimat.mjs";
import { GECERLI, OLAYLAR, YOL } from "./tablo.mjs";

function dene(uret, etiket) {
  let kabulEdilenGecersiz = 0;
  let reddedilenGecerli = 0;
  const satirlar = [];
  for (const [durumAdi, yol] of Object.entries(YOL)) {
    const isaret = [];
    for (const olay of OLAYLAR) {
      const t = uret("TS-1");
      for (const o of yol) t.olay(o);
      const kabul = t.olay(olay);
      const gecerli = Object.hasOwn(GECERLI[durumAdi], olay);
      if (kabul && gecerli === false) kabulEdilenGecersiz += 1;
      if (kabul === false && gecerli) reddedilenGecerli += 1;
      isaret.push(kabul === gecerli ? "." : kabul ? "K" : "R");
    }
    satirlar.push(`  ${durumAdi.padEnd(12)} ${isaret.join(" ")}`);
  }
  console.log(`${etiket} (${OLAYLAR.join(" ")}):`);
  for (const s of satirlar) console.log(s);
  console.log(`  ${etiket.padEnd(12)} kabul edilen gecersiz=${kabulEdilenGecersiz}  reddedilen gecerli=${reddedilenGecerli}`);
}

dene(bayrakTeslimat, "bayrak");
dene(durumTeslimat, "durum");
```

```
bayrak (topla yolaCik dagitimaVer teslimEt iadeEt):
  olusturuldu  . . . . .
  toplandi     . . . . .
  yolda        . . . K .
  dagitimda    . . . . .
  teslim       . . . . K
  iade         . . K . .
  bayrak       kabul edilen gecersiz=3  reddedilen gecerli=0
durum (topla yolaCik dagitimaVer teslimEt iadeEt):
  olusturuldu  . . . . .
  toplandi     . . . . .
  yolda        . . . . .
  dagitimda    . . . . .
  teslim       . . . . .
  iade         . . . . .
  durum        kabul edilen gecersiz=0  reddedilen gecerli=0
```

Üçe karşı sıfır. Bayrak sürümü hiçbir geçerli geçişi reddetmiyor, yani kabaca bozuk değil;
fazla **izin veriyor**. Üç `K` işareti üç somut hatadır: dağıtıma çıkmamış bir gönderi teslim
edilebiliyor, teslim edilmiş bir gönderi iade edilebiliyor, iade edilmiş bir gönderi dağıtıma
verilebiliyor. Üçünün kaynağı aynı: her olay kendi koşulunu kendisi kuruyor ve hiçbiri
"şu anda hangi durumdayım" sorusunu tek bir yerden sormuyor.

## Karmaşıklık ve Temsil Uzayı

```js
// karmasiklik.mjs — karar noktasi sayarak cevrimsel karmasiklik verir
import { readFileSync, readdirSync } from "node:fs";

const KARAR = /\bif\b|&&|\|\||\?|\bcase\b|\bwhile\b|\bfor\b/g;

for (const dizin of ["bayrak", "durum"]) {
  let karar = 0;
  let satir = 0;
  const dosyalar = readdirSync(dizin).sort();
  for (const d of dosyalar) {
    const metin = readFileSync(`${dizin}/${d}`, "utf8").replace(/^\/\/.*$/gm, "");
    karar += (metin.match(KARAR) ?? []).length;
    satir += metin.split("\n").filter((s) => s.trim().length > 0).length;
  }
  console.log(`${dizin.padEnd(7)} dosya=${dosyalar.length}  satir=${satir}  karar noktasi=${karar}  cevrimsel karmasiklik=${karar + 1}`);
}
```

```
bayrak  dosya=1  satir=47  karar noktasi=21  cevrimsel karmasiklik=22
durum   dosya=2  satir=26  karar noktasi=1  cevrimsel karmasiklik=2
```

Yirmi ikiye karşı iki. Fark, kararların koddan veriye taşınmasından gelir: geçiş kuralları
artık dallanma değil, eşleme girdisidir. İkinci sayı bunun neden hataya yer bırakmadığını
gösterir.

```js
// bedel.mjs — temsil uzayi ve gecis hedefi yanlis yazildiginda ortaya cikan gecikme
import { cpSync, readFileSync, writeFileSync } from "node:fs";
import { DURUMLAR } from "./durum/durumlar.mjs";

const BAYRAK = 5;
console.log(`bayrak: temsil edilebilir birlesim=${2 ** BAYRAK}  gecerli durum=${Object.keys(DURUMLAR).length}`);
console.log(`durum : temsil edilebilir birlesim=${Object.keys(DURUMLAR).length}  gecerli durum=${Object.keys(DURUMLAR).length}`);

cpSync("durum", "durum-hatali", { recursive: true });
writeFileSync(
  "durum-hatali/durumlar.mjs",
  readFileSync("durum/durumlar.mjs", "utf8").replace('dagitimaVer: "dagitimda"', 'dagitimaVer: "dagitimdaa"'),
);

const t = (await import("./durum-hatali/teslimat.mjs")).teslimat("TS-3");
let olayNo = 0;
for (const o of ["topla", "yolaCik", "dagitimaVer", "teslimEt"]) {
  olayNo += 1;
  try {
    const kabul = t.olay(o);
    console.log(`olay ${olayNo} ${o.padEnd(12)} kabul=${kabul ? 1 : 0}`);
  } catch (e) {
    console.log(`olay ${olayNo} ${o.padEnd(12)} ${e.constructor.name} firlatildi`);
    break;
  }
}
```

```
bayrak: temsil edilebilir birlesim=32  gecerli durum=6
durum : temsil edilebilir birlesim=6  gecerli durum=6
olay 1 topla        kabul=1
olay 2 yolaCik      kabul=1
olay 3 dagitimaVer  kabul=1
olay 4 teslimEt     TypeError firlatildi
```

Otuz ikiye karşı altı. Beş bağımsız bayrak otuz iki birleşim üretir ve bunların yirmi altısı
hiçbir gerçek duruma karşılık gelmez; yolda olmadan dağıtımda olmak ya da hem teslim hem iade
olmak temsil edilebilir. Durum nesnesi tek bir değişken tuttuğu için temsil uzayı geçerli
durum kümesine eşittir.

Çıktının son dört satırı bedelin ilk kalemidir. Geçiş hedefi bir harf yanlış yazıldığında
`dagitimaVer` olayı **kabul edilir**, hata ancak bir sonraki olayda ortaya çıkar. Geçişler veri
olduğu için hedef adları kod değildir ve yanlış ad yazıldığı yerde değil, kullanıldığı yerde
patlar. Bedelin ikinci kalemi dosya sayısıdır: bir dosyaya karşı iki dosya, ve bir olayın kabul
edilip edilmediğini anlamak için ikisini de okumak gerekir.

## Yeni Durum Eklendiğinde

Adres teyidi bekleyen gönderiler için `beklemede` durumu ve iki yeni olay isteniyor:
yoldaki gönderi `beklet` ile bekletiliyor, `devamEt` ile yeniden yola çıkıyor. Betik değişikliği
iki sürüme de uygular, değişen satırları sayar ve aynı olay dizisini ikisinde de koşar.

```js
// yenidurum.mjs — "beklemede" durumunu iki surume de ekler ve degisimi olcer
import { cpSync, readFileSync, writeFileSync } from "node:fs";

cpSync("bayrak", "bayrak-yeni", { recursive: true });
cpSync("durum", "durum-yeni", { recursive: true });

const degistir = (yol, arayan, yeni) => {
  const eski = readFileSync(yol, "utf8");
  writeFileSync(yol, eski.replace(arayan, yeni));
};

degistir(
  "bayrak-yeni/teslimat.mjs",
  "  const d = { toplandi: false, yolda: false, dagitimda: false, teslim: false, iade: false };",
  "  const d = { toplandi: false, yolda: false, dagitimda: false, teslim: false, iade: false, beklemede: false };",
);
degistir(
  "bayrak-yeni/teslimat.mjs",
  "    d.iade ? \"iade\" : d.teslim ? \"teslim\" : d.dagitimda ? \"dagitimda\" : d.yolda ? \"yolda\" : d.toplandi ? \"toplandi\" : \"olusturuldu\";",
  "    d.iade ? \"iade\" : d.teslim ? \"teslim\" : d.beklemede ? \"beklemede\" : d.dagitimda ? \"dagitimda\" : d.yolda ? \"yolda\" : d.toplandi ? \"toplandi\" : \"olusturuldu\";",
);
degistir(
  "bayrak-yeni/teslimat.mjs",
  '      if (ad === "dagitimaVer") {\n        if (d.yolda && d.dagitimda === false) {',
  `      if (ad === "beklet") {
        if (d.yolda && d.beklemede === false && d.dagitimda === false) {
          d.beklemede = true;
          return true;
        }
        return false;
      }
      if (ad === "devamEt") {
        if (d.beklemede) {
          d.beklemede = false;
          return true;
        }
        return false;
      }
      if (ad === "dagitimaVer") {
        if (d.yolda && d.dagitimda === false && d.beklemede === false) {`,
);

degistir(
  "durum-yeni/durumlar.mjs",
  '  yolda: durum("yolda", { dagitimaVer: "dagitimda", iadeEt: "iade" }),',
  `  yolda: durum("yolda", { dagitimaVer: "dagitimda", iadeEt: "iade", beklet: "beklemede" }),
  beklemede: durum("beklemede", { devamEt: "yolda", iadeEt: "iade" }),`,
);

const fark = (a, b) => {
  const x = readFileSync(a, "utf8").split("\n");
  const y = readFileSync(b, "utf8").split("\n");
  const kume = new Set(x.map((s) => s.trim()));
  return y.filter((s) => s.trim().length > 0 && kume.has(s.trim()) === false).length;
};

console.log(`bayrak: eklenen/degisen satir=${fark("bayrak/teslimat.mjs", "bayrak-yeni/teslimat.mjs")}`);
console.log(`durum : eklenen/degisen satir=${fark("durum/durumlar.mjs", "durum-yeni/durumlar.mjs")}`);

const b = (await import("./bayrak-yeni/teslimat.mjs")).teslimat("TS-2");
const c = (await import("./durum-yeni/teslimat.mjs")).teslimat("TS-2");
for (const t of [b, c]) {
  const iz = [];
  for (const o of ["topla", "yolaCik", "beklet", "dagitimaVer", "devamEt", "dagitimaVer", "teslimEt"]) {
    iz.push(`${o}=${t.olay(o) ? "1" : "0"}`);
  }
  console.log(`${t.durumAdi().padEnd(10)} ${iz.join(" ")}`);
}
```

```
bayrak: eklenen/degisen satir=9
durum : eklenen/degisen satir=2
teslim     topla=1 yolaCik=1 beklet=1 dagitimaVer=0 devamEt=1 dagitimaVer=1 teslimEt=1
teslim     topla=1 yolaCik=1 beklet=1 dagitimaVer=0 devamEt=1 dagitimaVer=1 teslimEt=1
```

Dokuz satıra karşı iki satır, ve iki sürüm aynı olay dizisinde aynı kabul dizisini ve aynı son
durumu veriyor. Dokuz satırın bir kısmı var olan bir koşulun içine giriyor — `dagitimaVer`
koşuluna üçüncü bir denetim eklendi — yani yeni durum eski geçişlerin davranışını da riske
atıyor. Durum sürümünde eski geçiş satırlarına dokunulmadı.

## Özet

- Durum kalıbı her durumu kendi geçiş kümesini bilen bir nesne yapar; teslimat nesnesi
  hangi olayın geçerli olduğunu bilmez, elindeki durum nesnesine sorar.
- Otuz durum-olay çiftinin denenmesinde bayrak sürümü 3 geçersiz geçişi kabul etti, durum
  sürümü 0; iki sürüm de hiçbir geçerli geçişi reddetmedi.
- Çevrimsel karmaşıklık 22'den 2'ye indi; temsil edilebilir birleşim sayısı 32'den 6'ya indi,
  yani 26 anlamsız bayrak birleşimi ortadan kalktı.
- Yeni bir durum ve iki olay eklendiğinde bayrak sürümünde 9, durum sürümünde 2 satır değişti;
  iki sürüm aynı olay dizisinde aynı sonucu verdi.
- Bedel: dosya sayısı 1'den 2'ye çıktı ve geçiş hedefi yanlış yazıldığında hata yazıldığı
  olayda değil bir sonraki olayda ortaya çıktı.

## Sonraki Adım

Durum nesnesi bir soruyu tek bir yerden yanıtlıyor: bu olay burada geçerli mi. Kitaplığın
ücret düzeltmeleri tarafında ise 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
içine gömülü. Sonraki ders bu gövdenin karar noktalarını sayar, kural sırası değiştiğinde
düzenlenen satırları ölçer, sonra isteği işleyiciler arasında aktaran bir zincir kurup aynı
sayıları yeniden hesaplar.
