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

# Kalıp Kavramı

Bir kalıbın problem, çözüm ve sonuçlar üçlüsü olarak tanımlanması: gönderi kitaplığında iki ayrı dersten tanıdık olan kayıt çözümünün taşıyıcı seçimine uygulanması, aynı çıktıyı veren iki sürümün dosya sayısı, ithal kenarı sayısı, tür adının geçtiği yer sayısı ve en uzun ithal yolu ile ölçülmesi, kazancın ve bedelin ayrı ayrı sayılması.

Tasarım İlkeleri kursu bir ölçüt kümesiyle kapandı: sınır dar olsun, bağ kararlı yöne aksın,
birlikte değişenler bir arada dursun. Bu cümlelerin hiçbiri bir çözüm vermez. Bir modülün
açık–kapalı ilkesini karşılamadığı ölçülebilir, ama ilke o modülün yerine ne yazılacağını
söylemez. Kararı veren şey, aynı problemin daha önce çözülmüş olmasıdır.

Aynı problem farklı kod tabanlarında defalarca aynı biçimde çözülmüşse, o çözümün bir adı,
bilinen bir uygulanış biçimi ve bilinen sonuçları vardır. **Kalıp** (pattern) budur: bir kod
parçası değil, tekrar eden bir problemin adlandırılmış çözümü ve o çözümün getirdiği
sonuçların listesi. Bu ders üçlünün parçalarını tanımlar, gönderi ücretlendirme kitaplığında
daha önce iki kez bulunmuş bir çözümü taşıyıcı seçimine uygular ve sonuçlarını sayar.

## Aynı Çözümün İkinci Kez Bulunması

Temiz Kod kursunun Doğru Yapıyı Seçmek dersinde uzun bir koşul zinciri bir tabloya taşınmıştı;
tablo, tür adını davranışa eşleyen bir **kayıt** idi. Tasarım İlkeleri kursunun Açık–Kapalı
İlkesi dersinde aynı yapı yeniden çıktı: tarife türü `switch` ile çözülmek yerine tarifeler
kendilerini bir kayda yazdı, hesap modülü tür adlarını hiç bilmedi.

İki dersin problemi aynı değildi. Birincisi bir gövdenin karar noktası sayısını düşürmek
istiyordu, ikincisi yeni bir tür eklendiğinde var olan bir dosyanın düzenlenmesini
engellemek. Buna karşın bulunan çözümün yapısı harfi harfine aynıydı: bir eşleme tablosu,
tabloya yazan bir işlev, tablodan okuyan bir işlev ve tabloyu dolduran tek bir bileşim kökü.
Aynı çözümün ayrı baskılar altında ikinci kez bulunması, o çözümün adlandırılmayı hak
ettiğinin işaretidir.

## Üçlünün Parçaları

Bir kalıp anlatısı üç bölümden oluşur; her bölüm ayrı bir soruya cevap verir.

| Parça | Sorusu | Kayıt çözümünde karşılığı |
|---|---|---|
| Problem | Hangi baskı altında ortaya çıkar | Tür sayısı artıyor ve her yeni tür var olan dosyaları düzenletiyor |
| Çözüm | Hangi parçalar hangi ilişkiyle durur | Ad–davranış tablosu, yazan işlev, okuyan işlev, dolduran bileşim kökü |
| Sonuçlar | Neyi iyileştirir, neyi kötüleştirir | Tür adının geçtiği yer sayısı düşer; dosya ve ithal kenarı sayısı artar |

Üçlünün önünde bir **bağlam** cümlesi durur: kalıbın uygulanabilir olduğu koşul. Kayıt
çözümünün bağlamı, türlerin çalışma zamanında ada göre seçilmesidir. Türler derleme
zamanında belliyse ve seçim hiç yapılmıyorsa problem yoktur, dolayısıyla kalıp da yoktur.

Üç parçadan en çok atlananı sonuçlardır. Bir kalıbı "uygulamak" onun yapısını kurmak değil,
sonuçlarını kabul etmektir; bu yüzden sonuçlar sayılmadan kalıp seçimi yapılamaz.

## Taşıyıcı Seçiminin İki Sürümü

Kitaplıkta yeni bir problem var: bir gönderi için birden çok taşıyıcı teklifi üretilecek. Her
taşıyıcının kendi ücreti ve kendi teslim süresi var. Birinci sürümde taşıyıcı adı iki ayrı
`switch` ile çözülür.

```js
// sec/tasiyici.mjs — tasiyici adi iki ayri switch ile cozulur
export function ucret(ad, gonderi) {
  switch (ad) {
    case "yurtici":
      return 3900 + Math.ceil(gonderi.agirlik) * 900;
    case "hizli":
      return 6400 + Math.ceil(gonderi.agirlik) * 1500;
    default:
      throw new RangeError(`bilinmeyen tasiyici: ${ad}`);
  }
}

export function sureGun(ad) {
  switch (ad) {
    case "yurtici":
      return 3;
    case "hizli":
      return 1;
    default:
      throw new RangeError(`bilinmeyen tasiyici: ${ad}`);
  }
}
```

```js
// sec/teklif.mjs — teklif satiri; tasiyici adlarini kendisi de sayar
import { ucret, sureGun } from "./tasiyici.mjs";

export const ADLAR = ["yurtici", "hizli"];
export const satir = (ad, g) => `${ad}: ${ucret(ad, g)} kurus / ${sureGun(ad)} gun`;
```

```js
// sec/kur.mjs — ornek gonderi icin butun tasiyici tekliflerini yazar
import { ADLAR, satir } from "./teklif.mjs";

export const GONDERI = { agirlik: 2.4, adres: "34100" };
for (const ad of ADLAR) console.log(satir(ad, GONDERI));
```

```sh
node sec/kur.mjs
```

```
yurtici: 6600 kurus / 3 gun
hizli: 10900 kurus / 1 gun
```

İkinci sürüm kayıt çözümünü uygular. Her taşıyıcı kendi dosyasında bir nesne olarak durur ve
kendini kayda yazar.

```js
// kayit/kayit.mjs — tasiyici kaydi: her tasiyici kendini bu tabloya yazar
const TASIYICILAR = new Map();

export function kaydet(tasiyici) {
  TASIYICILAR.set(tasiyici.ad, tasiyici);
}

export const adlar = () => [...TASIYICILAR.keys()];
export function tasiyici(ad) {
  const t = TASIYICILAR.get(ad);
  if (t === undefined) throw new RangeError(`bilinmeyen tasiyici: ${ad}`);
  return t;
}
```

```js
// kayit/tasiyicilar/yurtici.mjs — yurtici tasiyici
import { kaydet } from "../kayit.mjs";

kaydet({
  ad: "yurtici",
  ucret: (g) => 3900 + Math.ceil(g.agirlik) * 900,
  sureGun: () => 3,
});
```

```js
// kayit/tasiyicilar/hizli.mjs — hizli tasiyici
import { kaydet } from "../kayit.mjs";

kaydet({
  ad: "hizli",
  ucret: (g) => 6400 + Math.ceil(g.agirlik) * 1500,
  sureGun: () => 1,
});
```

```js
// kayit/teklif.mjs — teklif satiri; hicbir tasiyici adi gecmez
import { tasiyici } from "./kayit.mjs";

export const satir = (ad, g) => {
  const t = tasiyici(ad);
  return `${ad}: ${t.ucret(g)} kurus / ${t.sureGun()} gun`;
};
```

```js
// kayit/kur.mjs — bilesim koku: hangi tasiyicilarin yuklendigini yalniz burasi bilir
import "./tasiyicilar/yurtici.mjs";
import "./tasiyicilar/hizli.mjs";
import { adlar } from "./kayit.mjs";
import { satir } from "./teklif.mjs";

export const GONDERI = { agirlik: 2.4, adres: "34100" };
for (const ad of adlar()) console.log(satir(ad, GONDERI));
```

```sh
node kayit/kur.mjs
```

```
yurtici: 6600 kurus / 3 gun
hizli: 10900 kurus / 1 gun
```

Çıktılar aynı. Fark yalnızca sonuçlardadır ve sonuçlar sayılabilir.

## Sonuçların Sayılması

Aşağıdaki ölçer bir dizindeki modülleri okur ve dört sayı verir: dosya sayısı, ithal kenarı
sayısı, verilen tür adlarının dizgi olarak geçtiği yer sayısı ve ithal çizgesindeki en uzun
yolun uzunluğu. İlk iki sayı kalıbın bedelini, üçüncüsü kazancını, dördüncüsü dolaylılık
basamağını ölçer.

```js
// olc.mjs — bir tasarimin dosya, ithal kenari, tur adi ve dolaylilik olculeri
import { readdirSync, readFileSync } from "node:fs";
import { dirname, join, relative, resolve } from "node:path";

const kok = process.argv[2];
const TURLER = process.argv.slice(3);

const topla = (d) =>
  readdirSync(d, { withFileTypes: true }).flatMap((e) =>
    e.isDirectory() ? topla(join(d, e.name)) : e.name.endsWith(".mjs") ? [join(d, e.name)] : []);

const metin = new Map(topla(kok).sort().map((y) => [relative(kok, y), readFileSync(y, "utf8")]));

const kenar = new Map();
for (const [ad, m] of metin) {
  kenar.set(ad, [...m.matchAll(/(?:from|import)\s*["'](\.[^"']+)["']/g)]
    .map((x) => relative(kok, resolve(dirname(join(kok, ad)), x[1]))));
}
const kenarSayisi = [...kenar.values()].reduce((t, h) => t + h.length, 0);

let turYeri = 0;
for (const [, m] of metin)
  for (const t of TURLER) turYeri += [...m.matchAll(new RegExp(`"${t}"`, "g"))].length;

const derinlik = (ad, gorulen = new Set()) => {
  const alt = (kenar.get(ad) ?? []).filter((h) => !gorulen.has(h));
  return alt.length === 0 ? 0 : 1 + Math.max(...alt.map((h) => derinlik(h, new Set([...gorulen, ad]))));
};
const enUzun = Math.max(...[...kenar.keys()].map((a) => derinlik(a)));

console.log(`${kok.padEnd(6)} dosya=${metin.size} ithal-kenari=${kenarSayisi} ` +
  `tur-adi-gecen-yer=${turYeri} en-uzun-ithal-yolu=${enUzun}`);
```

```sh
node olc.mjs sec yurtici hizli
node olc.mjs kayit yurtici hizli
```

```
sec    dosya=3 ithal-kenari=2 tur-adi-gecen-yer=6 en-uzun-ithal-yolu=2
kayit  dosya=5 ithal-kenari=7 tur-adi-gecen-yer=2 en-uzun-ithal-yolu=2
```

Kazanç üçüncü sayıda. Birinci sürümde iki taşıyıcı adı altı yerde geçiyor: her ad iki
`switch` içinde birer kez ve `ADLAR` dizisinde bir kez. Bu, Temiz Kod kursunda tanımlanan
bilgi yinelemesidir; taşıyıcı listesi üç ayrı yerde ayrı ayrı tutulur ve üçünün eşit
kalması hiçbir denetimle güvence altında değildir. İkinci sürümde her ad bir kez, yalnız
kendi dosyasında geçer.

Bedel ilk iki sayıda. Dosya sayısı üçten beşe, ithal kenarı sayısı ikiden yediye çıktı. İkinci
sayı, Bileşen Bağlaşım İlkeleri dersinde sayılan bağ sayısının aynısıdır: çizgeye beş kenar
eklendi ve bunların dördü tek bir dosyaya, bileşim köküne bağlıdır.

Dördüncü sayı kalıbın bedelinin nerede görünmediğini gösteriyor: en uzun ithal yolu iki
sürümde de 2. Yani dolaylılık, dosyalar arasındaki yol uzunluğunda değil, çalışma zamanındaki
çözümlemede artıyor. Birinci sürümde `ucret` çağrısının hangi gövdeye gittiği dosyayı okuyarak
bulunur; ikinci sürümde çağrının hedefi kayda hangi dosyaların yazdığına bağlıdır ve bu bilgi
yalnız bileşim kökünde durur. Tek bir ölçü kalıbın bedelini vermeye yetmez.

## Problem Sınıflarına Göre Bölünme

Kalıplar çözdükleri problem türüne göre ayrılır ve bu kurs bölümlerini o ayrımdan alır.
**Yaratımsal kalıplar** bir nesnenin kim tarafından ve nasıl kurulacağı sorusunu çözer.
**Yapısal kalıplar** var olan nesnelerin nasıl birleştirileceğini düzenler. **Davranışsal
kalıplar** nesneler arasındaki sorumluluk ve iletişim dağılımını ele alır. **Kurumsal
uygulama kalıpları** iş mantığı ile kalıcılık arasındaki sınıra aittir; bunların bir kısmı
Backend Geliştirme müfredatının Veri Erişim Katmanı kursunda kurulmuştu ve burada kalıp
kataloğu düzeyinde yeniden ele alınır.

Kayıt çözümü bu sınıflamada yaratımsal tarafa düşer, çünkü çözdüğü şey davranışın kendisi
değil, davranışı taşıyan nesnenin nereden geleceğidir. Sonraki iki ders bu soruyu daha keskin
biçimde sorar.

## Özet

- Kalıp bir kod parçası değil, tekrar eden bir problemin adlandırılmış çözümüdür; anlatısı
  problem, çözüm ve sonuçlar üçlüsünden oluşur ve önünde bir bağlam koşulu durur.
- Bir çözümün kalıp sayılması, aynı yapının ayrı baskılar altında yeniden bulunmasına
  bağlıdır; kayıt çözümü Temiz Kod ve Tasarım İlkeleri kurslarında iki ayrı problem için
  birbirinin aynı biçimde çıktı.
- Aynı teklif satırını üreten iki sürümde tür adının geçtiği yer sayısı 6'dan 2'ye düştü;
  bu kalıbın kazancıdır.
- Aynı karşılaştırmada dosya sayısı 3'ten 5'e, ithal kenarı sayısı 2'den 7'ye çıktı; bu
  kalıbın bedelidir ve kazançla aynı ölçü kümesinden okunur.
- En uzun ithal yolu iki sürümde de 2 kaldı: bir kalıbın dolaylılık bedeli tek bir ölçüyle
  görünmez, çağrı hedefinin çalışma zamanında çözülmesi ayrıca hesaba katılır.

## Sonraki Adım

Kayıt sürümünde teklif modülü taşıyıcı adlarını bilmiyor, ama hangi adın hangi nesneye
karşılık geldiğini yine bir dizgi üzerinden soruyor ve nesnenin kendisini kayıt kuruyor.
Nesne kurma işi tek bir çağrıya sığmadığında — taşıyıcının bir ücret hesabı, bir rota
planlayıcısı ve bir etiket biçimi varsa — kayda yazılacak şeyin kendisi bir kurulum işlemine
dönüşür. Sonraki ders bu kurulumu bir yönteme çeker: alt tipin hangi nesneyi üreteceğine
kendisinin karar verdiği fabrika yöntemini tanımlar, birbirine uyması gereken nesneleri
birlikte üreten soyut fabrikayı ekler ve yeni bir taşıyıcı ailesi eklendiğinde düzenlenen
dosya ve satır sayısını iki sürümde sayar.
