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

# Kurucu

Çok alanlı bir nesnenin adım adım kurulması: sekiz sıralı parametreli çağrı ile adlandırılmış adımlardan oluşan kurucunun aynı bozma kümesiyle karşılaştırılması, yer değiştiren argümanların ve eksik alanların kaç durumda sessizce kabul edildiğinin sayılması, geçirilen parametre sayısının düşüşü ve kalıbın satır cinsinden bedeli.

Soyut fabrika üç parçayı birlikte döndürdü, ama parçaların her biri tek ifadeyle kurulabilen
bir sabitti. Kitaplığın merkezindeki nesne öyle değil: bir gönderinin ağırlığı, üç boyutu,
gönderim ve teslim ili, beyan edilen değeri ve varsa sözleşme numarası var. Sekiz alan tek bir
çağrıya sıkıştırıldığında çağrı yerinde hangi sayının hangi alana gittiği görünmez olur.

**Kurucu** (builder) kurulumu adlandırılmış adımlara böler ve nesneyi ancak son adımda üretir.
Bu dersin ölçüsü kurulumun ne kadar okunaklı göründüğü değil: aynı bozma kümesi iki tasarıma
uygulanır ve kaç bozmanın **sessizce kabul edildiği** sayılır. Kalıbın bedeli de aynı biçimde,
satır sayısıyla ölçülür.

## Sekiz Sıralı Parametre

Birinci sürümde gönderi tek bir çağrıyla kurulur. Hiçbir alan denetlenmez; çağrıya ne
verilirse nesneye o girer.

```js
// dogrudan/gonderi.mjs — sekiz sirali parametre; hicbir alan denetlenmiyor
export function gonderiYap(agirlik, en, boy, yukseklik, gonderimIli, teslimIli, deger, sozlesmeNo) {
  return { agirlik, en, boy, yukseklik, gonderimIli, teslimIli, deger, sozlesmeNo };
}

const BOLGE = { "34": 100, "06": 115, "65": 140 };
const desi = (g) => Math.ceil((g.en * g.boy * g.yukseklik) / 5000);

export function ucret(g) {
  const taban = 3900 + Math.ceil(Math.max(g.agirlik, desi(g))) * 900;
  const katsayi = Math.max(BOLGE[g.gonderimIli] ?? 160, BOLGE[g.teslimIli] ?? 160);
  const sigorta = Math.round(g.deger * 0.004);
  const indirim = typeof g.sozlesmeNo === "string" ? 500 : 0;
  return Math.round((taban * katsayi) / 100) + sigorta - indirim;
}
```

Ücret sekiz alanın yedisini kullanır: ağırlık ile üç boyut taban ücrete, iki il kodu bölge
katsayısına, beyan değeri sigortaya, sözleşme numarası indirime girer. Yani bir alanın yanlış
yere gitmesi ücreti değiştirir.

## Adlandırılmış Adımlar

İkinci sürümde her adım bir yöntemdir, kendi alanını denetler ve kurucunun kendisini döndürür.
Nesne `kur` çağrısında üretilir; zorunlu alanların eksiksizliği yalnız orada, tek bir noktada
denetlenir.

```js
// kurucu/gonderi.mjs — adlandirilmis adimlar; her adim kendi alanini denetler
const BOLGE = { "34": 100, "06": 115, "65": 140 };
const IL = /^[0-9]{2}$/;
const ZORUNLU = ["agirlik", "en", "boy", "yukseklik", "gonderimIli", "teslimIli", "deger"];

const sayi = (x, ust) => typeof x === "number" && x > 0 && x <= ust;

export class GonderiKurucu {
  #alan = {};

  agirlik(kg) {
    if (!sayi(kg, 50)) throw new RangeError("agirlik 0-50 kg araliginda bir sayi olmali");
    this.#alan.agirlik = kg;
    return this;
  }

  olculer(en, boy, yukseklik) {
    if (![en, boy, yukseklik].every((x) => sayi(x, 200)))
      throw new RangeError("olcu 0-200 cm araliginda bir sayi olmali");
    Object.assign(this.#alan, { en, boy, yukseklik });
    return this;
  }

  adres(gonderimIli, teslimIli) {
    if (![gonderimIli, teslimIli].every((i) => typeof i === "string" && IL.test(i)))
      throw new RangeError("il kodu iki basamakli bir dizgi olmali");
    Object.assign(this.#alan, { gonderimIli, teslimIli });
    return this;
  }

  deger(kurus) {
    if (!Number.isInteger(kurus) || kurus < 0)
      throw new RangeError("deger negatif olmayan tam sayi olmali");
    this.#alan.deger = kurus;
    return this;
  }

  sozlesme(no) {
    if (typeof no !== "string" || !/^S[0-9]{4}$/.test(no))
      throw new RangeError("sozlesme numarasi S ve dort basamak olmali");
    this.#alan.sozlesmeNo = no;
    return this;
  }

  kur() {
    const eksik = ZORUNLU.filter((k) => this.#alan[k] === undefined);
    if (eksik.length > 0) throw new TypeError(`eksik alan: ${eksik.join(", ")}`);
    return Object.freeze({ sozlesmeNo: null, ...this.#alan });
  }
}

const desi = (g) => Math.ceil((g.en * g.boy * g.yukseklik) / 5000);

export function ucret(g) {
  const taban = 3900 + Math.ceil(Math.max(g.agirlik, desi(g))) * 900;
  const katsayi = Math.max(BOLGE[g.gonderimIli] ?? 160, BOLGE[g.teslimIli] ?? 160);
  const sigorta = Math.round(g.deger * 0.004);
  const indirim = typeof g.sozlesmeNo === "string" ? 500 : 0;
  return Math.round((taban * katsayi) / 100) + sigorta - indirim;
}
```

Üç ayrıntı kalıbın parçasıdır. Her adım `this` döndürür, böylece adımlar zincirlenebilir.
Zorunlu alan listesi tek bir yerdedir, dolayısıyla eksiklik denetimi de tek yerdedir. Üretilen
nesne dondurulmuştur; kurulum bittikten sonra alan değiştirilemez ve sözleşme numarası
verilmemişse varsayılan `null` değeri `kur` tarafından konur.

```js
// kur.mjs — ayni gonderi iki bicimde kurulur
import { gonderiYap, ucret as dogrudanUcret } from "./dogrudan/gonderi.mjs";
import { GonderiKurucu, ucret as kurucuUcret } from "./kurucu/gonderi.mjs";

const g1 = gonderiYap(2.4, 30, 40, 50, "34", "06", 180000, "S1042");
const g2 = new GonderiKurucu()
  .agirlik(2.4)
  .olculer(30, 40, 50)
  .adres("34", "06")
  .deger(180000)
  .sozlesme("S1042")
  .kur();

console.log(`dogrudan ${dogrudanUcret(g1)} kurus`);
console.log(`kurucu   ${kurucuUcret(g2)} kurus`);
```

```sh
node kur.mjs
```

```
dogrudan 17125 kurus
kurucu   17125 kurus
```

Aynı ücret. Bir çağrı satırı yedi satıra çıktı; kazancın bu satırlara değip değmediği
sayılabilir.

## Bozma Kümesinin Uygulanması

Aşağıdaki ölçer iki bozma kümesi üretir. Birincisi doğru değer dizisindeki her argüman çiftini
yer değiştirir: sekiz alan için 28 çift. İkincisi her alanı sırayla eksiltir: 8 çağrı. Her
bozulmuş çağrının sonucu dört sınıfa ayrılır — kurulum sırasında hata atanlar, sayı olmayan
bir sonuç verenler, doğru sonucun aynısını verenler ve hata atmadan farklı bir ücret üretenler.
Son sınıf sessizce kabul edilen yanlış kurulumlardır.

```js
// sinama.mjs — bozulmus cagrilari uretir ve sonuclarini siniflandirir
import { readFileSync } from "node:fs";
import { gonderiYap, ucret as dogrudanUcret } from "./dogrudan/gonderi.mjs";
import { GonderiKurucu, ucret as kurucuUcret } from "./kurucu/gonderi.mjs";

const DOGRU = [2.4, 30, 40, 50, "34", "06", 180000, "S1042"];

const dogrudanKur = (v) => dogrudanUcret(gonderiYap(...v));
const kurucuKur = (v) =>
  kurucuUcret(new GonderiKurucu().agirlik(v[0]).olculer(v[1], v[2], v[3])
    .adres(v[4], v[5]).deger(v[6]).sozlesme(v[7]).kur());

function sinifla(kur, degerler, beklenen) {
  let sonuc;
  try {
    sonuc = kur(degerler);
  } catch {
    return "hata";
  }
  if (!Number.isFinite(sonuc)) return "gecersiz";
  return sonuc === beklenen ? "ayni" : "sessiz";
}

function say(etiket, kur, kumeler) {
  const beklenen = kur(DOGRU);
  const sayim = { hata: 0, gecersiz: 0, ayni: 0, sessiz: 0 };
  for (const v of kumeler) sayim[sinifla(kur, v, beklenen)] += 1;
  console.log(`${etiket} cagri=${kumeler.length} hata=${sayim.hata} ` +
    `gecersiz=${sayim.gecersiz} ayni=${sayim.ayni} sessiz-yanlis=${sayim.sessiz}`);
}

const yerDegistirme = [];
for (let i = 0; i < DOGRU.length; i += 1)
  for (let j = i + 1; j < DOGRU.length; j += 1) {
    const v = [...DOGRU];
    [v[i], v[j]] = [v[j], v[i]];
    yerDegistirme.push(v);
  }

const eksikAlan = DOGRU.map((_, i) => DOGRU.map((d, k) => (k === i ? undefined : d)));

const enYuksekArite = (yol) =>
  Math.max(...[...readFileSync(yol, "utf8").matchAll(/\(([^)]*)\)\s*\{/g)]
    .map((m) => m[1].split(",").filter((s) => s.trim() !== "").length));

console.log(`dogrudan en-yuksek-parametre=${enYuksekArite("dogrudan/gonderi.mjs")}`);
console.log(`kurucu   en-yuksek-parametre=${enYuksekArite("kurucu/gonderi.mjs")}`);
say("dogrudan yer-degistirme", dogrudanKur, yerDegistirme);
say("kurucu   yer-degistirme", kurucuKur, yerDegistirme);
say("dogrudan eksik alan    ", dogrudanKur, eksikAlan);
say("kurucu   eksik alan    ", kurucuKur, eksikAlan);
```

```sh
node sinama.mjs
```

```
dogrudan en-yuksek-parametre=8
kurucu   en-yuksek-parametre=3
dogrudan yer-degistirme cagri=28 hata=0 gecersiz=5 ayni=4 sessiz-yanlis=19
kurucu   yer-degistirme cagri=28 hata=21 gecersiz=0 ayni=4 sessiz-yanlis=3
dogrudan eksik alan     cagri=8 hata=0 gecersiz=5 ayni=0 sessiz-yanlis=3
kurucu   eksik alan     cagri=8 hata=8 gecersiz=0 ayni=0 sessiz-yanlis=0
```

## Sayıların Okunması

İlk iki satır Bağlaşım Türleri dersinin ölçüsüdür: bir çağrıda geçirilen en yüksek parametre
sayısı. Sekizden üçe düştü ve kalan üç parametre aynı türden üç ölçüdür (`olculer`), yani
birlikte anlam taşırlar.

Yer değiştirme kümesinde birinci sürüm 28 bozmanın hiçbirinde hata atmadı. Beşinde sonuç sayı
olmadı — bu tespit edilebilir bir bozulmadır. Dördünde sonuç değişmedi. Kalan 19 bozma hata
atmadan farklı bir ücret üretti; bunların her biri, kod incelemesinde yakalanmazsa müşteriye
yanlış tutar olarak gider.

İkinci sürümde aynı 28 bozmanın 21'i kurulum anında hata attı, çünkü sayı bekleyen bir adıma
dizgi ya da aralık dışı bir sayı geldi. Sessizce kabul edilen bozma sayısı 19'dan 3'e indi.
Kalan üçü şu üç çiftin yer değiştirmesidir: ağırlık ile en, ağırlık ile boy, ağırlık ile
yükseklik. Üçü de aynı türden, aynı aralıkta sayılardır ve hiçbir tip denetimi bunları
ayırmaz. Bu artık kurucunun çözebileceği bir problem değildir; kilogram ile santimetreyi ayrı
tiplere taşımak gerekir, yani Veri Erişim Katmanı kursunda tanıtılan değer nesnesi.

Dördü değişmedi diye "doğru" sayılmaz. Bu dört bozmanın üçü boyutların desi çarpımındaki
simetrisidir: en, boy ve yükseklik birbirinin yerine geçtiğinde çarpım aynı kalır ve ücret
değişmez. Dördüncüsü gönderim ve teslim ilinin yer değiştirmesidir; bu tarife iki ilin bölge
katsayısının büyüğünü aldığı için ücret aynı çıkar. Ölçü o hatayı görmez, çünkü ücret o hataya
duyarlı değildir.

Eksik alan kümesi kalıbın ikinci kazancını verir. Birinci sürüm 8 eksiltmenin hiçbirinde hata
vermedi: beşi sayı olmayan bir ücret, üçü sessizce yanlış bir ücret üretti. İkinci sürümde
sekizinin sekizi `kur` çağrısında durdu ve hata iletisi hangi alanın eksik olduğunu adıyla
söyledi. Nesnenin değişmezini tek bir noktada denetlemenin ölçülebilir karşılığı budur.

## Bedel

Kalıbın maliyeti satır sayısında görünür.

```sh
for d in dogrudan/gonderi.mjs kurucu/gonderi.mjs; do
  echo "$d: $(grep -c '' $d) satir"
done
```

```
dogrudan/gonderi.mjs: 15 satir
kurucu/gonderi.mjs: 60 satir
```

Aynı nesneyi kuran modül 15 satırdan 60 satıra çıktı ve çağrı yeri bir satırdan yedi satıra
uzadı. Bedelin bir kısmı denetimlere ait, bir kısmı adım yöntemlerinin kendisine. Kurucu bu
yüzden alan sayısı azken uygulanmaz: iki üç alanlı bir nesnede yer değiştirme kümesi zaten
küçüktür ve 45 satırlık ek metnin karşılığı çıkmaz.

İkinci bir bedel zincirin yarım kalabilmesidir. `kur` çağrılmadığı sürece kurucu nesnesi elde
tutulabilir ve yarı kurulmuş bir durum taşır. Bu durumun dışarı sızmaması, `kur` dışında hiçbir
adımın nesne döndürmemesine bağlıdır; kalıbın doğru uygulanması bu kısıtla birlikte gelir.

## Özet

- Kurucu çok alanlı bir nesnenin kurulumunu adlandırılmış adımlara böler ve nesneyi yalnız son
  adımda üretir; zorunlu alan denetimi o tek noktada toplanır.
- Bir çağrıda geçirilen en yüksek parametre sayısı 8'den 3'e düştü ve kalan üç parametre aynı
  türden üç ölçüdür.
- 28 argüman yer değiştirmesinden sessizce kabul edilenlerin sayısı 19'dan 3'e indi; kalan üçü
  aynı türden sayıların yer değiştirmesidir ve ancak ayrı tiplerle önlenebilir.
- 8 eksik alan çağrısı birinci sürümde hiç hata vermedi, ikinci sürümde sekizi de hangi alanın
  eksik olduğunu adıyla söyleyerek durdu.
- Bedel 15 satırdan 60 satıra çıkan modül ve bir satırdan yedi satıra uzayan çağrı yeridir;
  alan sayısı azken kalıp bu bedeli karşılamaz.

## Sonraki Adım

Kurucu her nesneyi sıfırdan kuruyor. Oysa kitaplıkta kurulacak nesnelerin çoğu birbirinin
küçük bir sapmasıdır: sözleşmeli bir müşterinin tarifesi standart tarifenin yalnız iki alanı
değişmiş hâlidir, bir bölgeye özel tarife de öyle. Bu durumda bütün adımları yeniden yürütmek
hem çağrı satırını uzatır hem de standart tarifeye eklenen her yeni alanın türetilmiş
tarifelere elle taşınmasını gerektirir. Sonraki ders nesneyi kurmak yerine var olan bir nesneyi
kopyalayıp değiştirmeyi ele alır: standart tarifeye yeni bir alan eklendiğinde elle yazan ile
kopyalayan sürümlerde kaç türetilmiş tarifede alanın eksik kaldığını sayar ve sığ kopyanın
prototiple kaç alt nesne paylaştığını ölçer.
