---
title: 'Kalıp Kötüye Kullanımı'
source: 'https://academia.sh/tr/kurslar/tasarim-kaliplari/kalip-kotuye-kullanimi'
course: 'Tasarım Kalıpları'
language: tr
updated: '2026-08-17T18:10:38+00:00'
license: 'CC BY-SA 4.0'
---

# Kalıp Kötüye Kullanımı

Beş davranışsal kalıbın tek gerçekleştirimle uygulandığı sürümün kalıpsız sürümle aynı ölçülerle karşılaştırılması: dosya sayısı, anlamlı satır, ithal kapanışı, en karmaşık gövde ve çağrı derinliği bedelleri ile sıfır kalan kazançlar; konunun dokuz dersinde ölçülen eşiklerin toplu tablosu.

Dokuz derste dokuz kalıbın kazancı da bedeli de sayıldı ve ölçümlerin yapıldığı ölçeklerde —
strateji dört tarifede, zincir yedi kuralda, arabulucu dört alanda — kazanç her seferinde bedeli
aştı. Bu sayılar bir eşiğin varlığını gösteriyor, ama eşiğin **altında** ne olduğu henüz
ölçülmedi.

Bu ders aynı ölçüleri kalıbın aleyhine koşturur. Beş kalıp — strateji, gözlemci, şablon yöntem,
durum, sorumluluk zinciri — tek gerçekleştirimle uygulanır ve aynı davranışı üreten kalıpsız
sürümle karşılaştırılır. Kalıplı sürümde tek tarife, tek gözlemci, tek değişen adım, iki durum
ve tek işleyici var. Beklenen sonuç şudur: bedel kalemleri aynı kalır, kazanç kalemleri sıfırlanır.

## Aynı Davranış, İki Sürüm

Ücretlendirme şu adımlardan geçiyor: gönderi onaylanmış olmalı, taban ücret ağırlıktan
hesaplanmalı, bölge katsayısı uygulanmalı, hacim indirimi varsa düşülmeli, asgari ücret
uygulanmalı ve sonuç günlüğe yazılmalı.

```sh
mkdir -p duz kalipli

cat > kalipli/durumlar.mjs <<'SON'
// kalipli/durumlar.mjs — iki durumlu durum makinesi
const durum = (ad, ucretlenebilir) => ({ ad, ucretlenebilir });

export const DURUMLAR = { taslak: durum("taslak", false), onaylandi: durum("onaylandi", true) };
SON

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

export const SIRA = [hacim];
SON

cat > kalipli/zincir.mjs <<'SON'
// kalipli/zincir.mjs — zincir yurutucusu
export function indirim(kurallar, gonderi) {
  let oran = 0;
  for (const k of kurallar) {
    if (k.uygunMu(gonderi) === false) continue;
    oran += k.oran(gonderi);
    if (k.keser) break;
  }
  return oran;
}
SON

cat > kalipli/gozlemci.mjs <<'SON'
// kalipli/gozlemci.mjs — tek gozlemcili olay yayimi
export const yayinci = () => {
  const aboneler = [];
  return {
    abone: (d) => aboneler.push(d),
    bildir(olay) {
      for (const d of aboneler) d(olay);
    },
  };
};
SON
```

```js
// derinlik.mjs — o anki cagri yiginda kendi dosyalarimiza ait cerceve sayisi
export const derinlik = () => new Error().stack.split("\n").filter((s) => s.includes(".mjs:")).length;
```

```js
// veri.mjs — ucretlendirilecek gonderiler
export const GONDERILER = [
  { kod: "GN-1", agirlik: 4, bolge: "1", durum: "onaylandi" },
  { kod: "GN-2", agirlik: 25, bolge: "2", durum: "onaylandi" },
  { kod: "GN-3", agirlik: 9, bolge: "3", durum: "taslak" },
];
```

Kalıpsız sürüm tek dosyadır ve altı adımı sırayla yapar.

```js
// duz/ucretle.mjs — ayni davranis tek dosyada, kalipsiz
import { derinlik } from "../derinlik.mjs";

const BOLGE = { "1": 100, "2": 130, "3": 175 };

export function ucretlendirici(gunluk) {
  return {
    ucretle(gonderi) {
      if (gonderi.durum !== "onaylandi") return { hata: "onaylanmadi" };
      const d = derinlik();
      let tutar = Math.round(((2500 + 420 * Math.ceil(gonderi.agirlik)) * BOLGE[gonderi.bolge]) / 100);
      if (gonderi.agirlik >= 20) tutar -= Math.round((tutar * 8) / 100);
      tutar = Math.max(tutar, 3900);
      gunluk.push(`ucret ${gonderi.kod} ${tutar}`);
      return { tutar, derinlik: d };
    },
  };
}
```

Kalıplı sürümde her sorumluluk kendi nesnesindedir. Strateji tek gerçekleştirim taşır ve taban
ücreti hesaplarken çağrı derinliğini kaydeder.

```js
// kalipli/tarife.mjs — tek gerceklestirimi olan strateji
import { derinlik } from "../derinlik.mjs";

export const standart = {
  ad: "standart",
  asgari: 3900,
  ucretliAgirlik: (g) => Math.ceil(g.agirlik),
  tabanUcret(a) {
    this.sonDerinlik = derinlik();
    return 2500 + 420 * a;
  },
};
```

```js
// kalipli/sablon.mjs — tek adimi degisen sablon yontem
const BOLGE = { "1": 100, "2": 130, "3": 175 };

export function hesapla(tarife, gonderi, indirimOrani) {
  const a = tarife.ucretliAgirlik(gonderi);
  let tutar = Math.round((tarife.tabanUcret(a) * BOLGE[gonderi.bolge]) / 100);
  tutar -= Math.round((tutar * indirimOrani) / 100);
  return Math.max(tutar, tarife.asgari);
}
```

```js
// kalipli/kur.mjs — bilesim koku: bes kalibi birbirine baglar
import { standart } from "./tarife.mjs";
import { hesapla } from "./sablon.mjs";
import { DURUMLAR } from "./durumlar.mjs";
import { SIRA } from "./kurallar.mjs";
import { indirim } from "./zincir.mjs";
import { yayinci } from "./gozlemci.mjs";

export function ucretlendirici(gunluk) {
  const y = yayinci();
  y.abone((olay) => gunluk.push(`ucret ${olay.kod} ${olay.tutar}`));
  return {
    ucretle(gonderi) {
      const d = DURUMLAR[gonderi.durum];
      if (d === undefined || d.ucretlenebilir === false) return { hata: "onaylanmadi" };
      const tutar = hesapla(standart, gonderi, indirim(SIRA, gonderi));
      y.bildir({ kod: gonderi.kod, tutar });
      return { tutar, derinlik: standart.sonDerinlik };
    },
  };
}
```

## Davranış Eşitliği ve Çağrı Derinliği

```js
// kosum.mjs — iki surumu ayni gonderilerle kosar, tutar ve derinligi karsilastirir
import { GONDERILER } from "./veri.mjs";
import { ucretlendirici as duzUcret } from "./duz/ucretle.mjs";
import { ucretlendirici as kalipliUcret } from "./kalipli/kur.mjs";

const kosu = (kur) => {
  const gunluk = [];
  const u = kur(gunluk);
  const satir = GONDERILER.map((g) => {
    const r = u.ucretle(g);
    return r.hata === undefined ? `${g.kod}=${r.tutar}` : `${g.kod}=${r.hata}`;
  });
  const derinlik = u.ucretle(GONDERILER[0]).derinlik;
  return { satir, gunluk, derinlik };
};

const a = kosu(duzUcret);
const b = kosu(kalipliUcret);
console.log(`duz     ${a.satir.join(" ")}  gunluk=${a.gunluk.length}  cagri derinligi=${a.derinlik}`);
console.log(`kalipli ${b.satir.join(" ")}  gunluk=${b.gunluk.length}  cagri derinligi=${b.derinlik}`);
console.log(`sapma = ${a.satir.join(" ") === b.satir.join(" ") ? 0 : 1}`);
```

```
duz     GN-1=4180 GN-2=15548 GN-3=onaylanmadi  gunluk=3  cagri derinligi=4
kalipli GN-1=4180 GN-2=15548 GN-3=onaylanmadi  gunluk=3  cagri derinligi=6
sapma = 0
```

Sapma sıfır: iki sürüm aynı üç sonucu, aynı sayıda günlük satırını üretiyor. Beş kalıp
davranışa hiçbir şey eklemedi. Çağrı derinliği dörde karşı altı: taban ücretin hesaplandığı
satıra ulaşmak için iki çağrı daha geçildi.

## Bedelin ve Kazancın Aynı Tabloda Sayılması

```js
// olc.mjs — kalibin bedelini ve tek gerceklestirimdeki kazancini ayni olculerle sayar
import { readFileSync, readdirSync } from "node:fs";
import { dirname, join, normalize } from "node:path";
import { SIRA } from "./kalipli/kurallar.mjs";
import { DURUMLAR } from "./kalipli/durumlar.mjs";

const KARAR = /\bif\b|&&|\|\||\?|\bcase\b|\bwhile\b|\bfor\b|\bcontinue\b|\bbreak\b/g;
const govde = (yol) => readFileSync(yol, "utf8").replace(/^\/\/.*$/gm, "");

const ithaller = (yol) =>
  [...govde(yol).matchAll(/from "(\.[^"]+)"/g)].map((m) => normalize(join(dirname(yol), m[1])));

function kapanis(kok) {
  const gorulen = new Set([kok]);
  const yigit = [kok];
  while (yigit.length > 0) {
    for (const k of ithaller(yigit.pop())) {
      if (gorulen.has(k) === false) {
        gorulen.add(k);
        yigit.push(k);
      }
    }
  }
  return gorulen.size;
}

const olc = (dizin, giris) => {
  const dosyalar = readdirSync(dizin).sort();
  let satir = 0;
  let enKarmasik = 0;
  for (const d of dosyalar) {
    const m = govde(`${dizin}/${d}`);
    satir += m.split("\n").filter((s) => /[A-Za-z]/.test(s)).length;
    enKarmasik = Math.max(enKarmasik, (m.match(KARAR) ?? []).length + 1);
  }
  return { dosya: dosyalar.length, satir, enKarmasik, kapanis: kapanis(giris) };
};

const a = olc("duz", "duz/ucretle.mjs");
const b = olc("kalipli", "kalipli/kur.mjs");
console.log("olcu                        duz  kalipli");
console.log(`dosya sayisi                ${String(a.dosya).padEnd(4)} ${b.dosya}`);
console.log(`anlamli satir               ${String(a.satir).padEnd(4)} ${b.satir}`);
console.log(`ithal kapanisi              ${String(a.kapanis).padEnd(4)} ${b.kapanis}`);
console.log(`en karmasik govde           ${String(a.enKarmasik).padEnd(4)} ${b.enKarmasik}`);
console.log(`tek kosumda denenen tarife  1    1`);
console.log(`kayitli gozlemci sayisi     1    1`);
console.log(`zincirdeki isleyici sayisi  1    ${SIRA.length}`);
console.log(`durum makinesindeki durum   2    ${Object.keys(DURUMLAR).length}`);
```

```
olcu                        duz  kalipli
dosya sayisi                1    7
anlamli satir               12   47
ithal kapanisi              2    8
en karmasik govde           3    6
tek kosumda denenen tarife  1    1
kayitli gozlemci sayisi     1    1
zincirdeki isleyici sayisi  1    1
durum makinesindeki durum   2    2
```

Tablonun üst yarısı bedeldir, alt yarısı kazançtır. Bedel: yedi dosyaya karşı bir, kırk yedi
satıra karşı on iki, sekiz dosyalık ithal kapanışına karşı iki, en karmaşık gövdede altıya karşı
üç. Son sayı özellikle dikkat çekicidir: kalıplı sürümün **en karmaşık gövdesi**, kalıpsız
sürümün tek gövdesinden daha karmaşıktır. Zincir yürütücüsü tek işleyiciyi gezmek için bir
döngü, bir atlama ve bir kesme koşulu taşır; kalıpsız sürümde aynı indirim tek bir koşuldur.

Alt yarı sıfır kazanç gösteriyor. Strateji dersinde ölçülen "tek koşumda denenen tarife sayısı"
orada 1'e karşı 3'tü; burada 1'e karşı 1. Gözlemci dersinde giden bağ sayısı 4'ten 0'a inmişti;
burada bağ sayısı zaten 1 olduğu için indirilecek bir şey yok, buna karşılık kayıt yapılmadığında
sessizce hiçbir şey yapmama yolu açık kalıyor. Durum dersinde temsil uzayı 32'den 6'ya inmişti;
iki durumda ikiye karşı iki. Sorumluluk zinciri dersinde en karmaşık gövde yedi kuralda 9'dan
6'ya inmişti; tek kuralda 3'ten 6'ya **çıkıyor**.

## Eşik Tablosu

Dokuz dersin ölçümleri bir arada, her kalıbın karşılığını vermeye başladığı ölçekle birlikte:

| Kalıp | Ölçülen kazanç | Karşılığını verdiği ölçek |
|---|---|---|
| Strateji | tek koşumda denenen algoritma 1 → 3; yeni tarifede düzenlenen dosya 2 → 0 | ikinci algoritma |
| Gözlemci | giden bağ 4 → 0, ithal kapanışı 5 → 1 | ikinci dinleyici |
| Komut | geri alınan işlem 0 → 3, denetim izi kaydı 0 → 8 | geri alma ya da kayıt isteği |
| Şablon yöntem | yinelenen satır 6 → 0, yeni adımda dosya 3 → 1 | ikinci gerçekleştirim |
| Durum | geçersiz geçiş 3 → 0, karmaşıklık 22 → 2, temsil uzayı 32 → 6 | üçüncü durum |
| Sorumluluk zinciri | en karmaşık gövde 9 → 6 (yedi kuralda), 6 → 6 (dört kuralda) | beşinci kural |
| Yineleyici | gezinme kodu taşıyan yer 5 → 1; sıra değişiminde dosya 5 → 1 | ikinci gezinme işlemi |
| Ziyaretçi | sessizce yanlış sonuç veren işlem 3 → 0 | ikinci düğüm türü |
| Arabulucu | alanlar arası bağ 6 → 3, güncelleme çağrısı 8 → 4 | üçüncü alan |
| Anımsatıcı | dışarıdan erişilen iç alan 8 → 0, fiş alan sayısı 4 → 1 | durum saklama isteği |
| Yorumlayıcı | düzenlenen kaynak dosyası 1 → 0 | kuralı geliştirici dışında biri yazacaksa |
| Boş nesne | boş değer denetimi 2 → 0, hata fırlatan çağrı yeri 1 → 0 | ikinci çağrı yeri |

Sağ sütun tek bir cümlenin farklı biçimleridir: kalıp, **ayırdığı eksende ikinci bir örnek
gerçekten varsa** karşılığını verir. İkinci algoritma yoksa strateji bir dosya ve bir dolaylılık
basamağıdır; ikinci dinleyici yoksa gözlemci bir bileşim kökü ve sessiz bir hata yoludur; ikinci
gerçekleştirim yoksa şablon yöntem bir boş kancadır. Kalıbı gerekçelendiren şey kalıbın adı ya da
bilinirliği değil, sol sütundaki sayının sıfırdan farklı olmasıdır.

Bu ölçüt kalıbı sonradan uygulamayı da mümkün kılar. Kalıpsız sürüm, ikinci tarife geldiği gün
strateji sürümüne çevrilebilir; birinci dersteki ölçüm bu çevirinin iki dosya ve on üç satır
tuttuğunu göstermişti. Erken uygulanmış kalıbı geri almak ise yedi dosyayı bire indirmek
demektir ve bu iş, çevirinin tersinden pahalıdır. Kalıbın bedeli peşin, kazancı vadelidir; ölçü
tutulmadığında ödenen bedel geri alınmaz.

## Özet

- Beş davranışsal kalıp tek gerçekleştirimle uygulandığında kalıpsız sürümle birebir aynı
  sonucu üretti; sapma 0 çıktı, yani kalıplar davranışa hiçbir şey eklemedi.
- Bedel: dosya sayısı 1'den 7'ye, anlamlı satır 12'den 47'ye, ithal kapanışı 2'den 8'e, çağrı
  derinliği 4'ten 6'ya çıktı.
- Kalıplı sürümün en karmaşık gövdesi 6, kalıpsız sürümün tek gövdesi 3; tek işleyicili zincir
  karmaşıklığı düşürmek yerine yükseltti.
- Kazanç kalemleri sıfırlandı: tek koşumda denenen tarife 1'e karşı 1, kayıtlı gözlemci 1'e
  karşı 1, durum sayısı 2'ye karşı 2.
- Her kalıp, ayırdığı eksende ikinci bir örnek gerçekten varsa karşılığını verir; bedel peşin,
  kazanç vadelidir ve erken uygulanmış kalıbı geri almak uygulamaktan pahalıdır.

## Sonraki Adım

Bu konuya kadarki kalıpların hepsi **nesne düzeyinde** çalıştı: bir sorumluluk alındı, ayrı bir
nesneye verildi ve bedeli dolaylılık basamağıyla ödendi. Eşik tablosunun tamamı bu ölçekte
kuruldu — bir algoritma, bir dinleyici, bir düğüm türü. Sıradaki konu ölçeği bir basamak yukarı
çıkarır. Soru artık bir sorumluluğun hangi nesneye verileceği değil, bir uygulamanın **katman
düzeninde** iş mantığının nereye yerleştiği ve kalıcılıkla arasındaki sınırın hangi kalıplarla
kurulduğudur. İlk soru en temel olanıdır: aynı iş kuralı bir senaryo yordamının içinde mi
yaşamalı, yoksa veriyi taşıyan nesnenin üzerinde mi. Sonraki ders bu iki düzenlemeyi kuralın kaç
ayrı yerde uygulandığını sayarak karşılaştırır ve ikinci düzenlemenin dosya, ad ve dolaylılık
bedelini aynı biçimde ölçer.
