---
title: 'Fabrika Yöntemi ve Soyut Fabrika'
source: 'https://academia.sh/tr/kurslar/tasarim-kaliplari/fabrika-yontemi-ve-soyut-fabrika'
course: 'Tasarım Kalıpları'
language: tr
updated: '2026-08-17T18:10:38+00:00'
license: 'CC BY-SA 4.0'
---

# Fabrika Yöntemi ve Soyut Fabrika

Nesne yaratımının bir yönteme ve bir aile üretecine çekilmesi: üretecin hangi tarifeyi kuracağına alt tipin karar verdiği fabrika yöntemi, birbirine uyması gereken tarife, rota ve etiket üçlüsünü birlikte üreten soyut fabrika, çağrı yerinde eşleştirme yapılan sürümde sessizce kabul edilen yanlış birleşimlerin sayılması ve yeni bir taşıyıcı ailesi eklendiğinde düzenlenen dosya sayısının karşılaştırılması.

Kayıt çözümü taşıyıcı adlarını tek bir yere topladı, ama kayda yazılan şeyin nasıl kurulduğu
sorusuna hiç dokunmadı. Bir taşıyıcının tek bir ücret işlevi olduğu sürece kurulum tek bir
nesne sabitidir. Taşıyıcının bir ücret tarifesi, bir rota planı ve bir etiket biçimi olduğunda
kurulum üç parçanın birlikte hazırlanmasına dönüşür ve bu üç parça birbirine uymak zorundadır.

Bu ders iki kalıbı tanımlar. **Fabrika yöntemi** (factory method) tek bir nesnenin yaratımını
bir yönteme çeker ve o yöntemi alt tipin karşılamasını bekler. **Soyut fabrika** (abstract
factory) birbirine uyması gereken bir nesne ailesini tek çağrıda birlikte üretir. İkisinin
problemi de yaratım kararının nereye yazıldığıdır; ölçüleri farklıdır.

## Fabrika Yöntemi

Üreteç, teklif satırını hazırlayan tiptir. Hangi tarifenin kurulacağına kendisi karar
vermez: kurulumu `tarifeYap` yöntemine bırakır ve o yöntemi alt tipler kendi
gerçekleştirimiyle karşılar.

```js
// yontem/uretec.mjs — ureteci; hangi tarifenin kurulacagini alt tip belirler
export class TeklifUreteci {
  tarifeYap() {
    throw new TypeError(`${this.constructor.name} tarifeYap yontemini karsilamadi`);
  }

  teklif(gonderi) {
    const t = this.tarifeYap();
    return `${t.ad}: ${t.ucret(gonderi)} kurus`;
  }
}
```

```js
// yontem/kur.mjs — iki alt tip ayni yontemi kendi tarifesiyle karsilar
import { TeklifUreteci } from "./uretec.mjs";

class YurticiUreteci extends TeklifUreteci {
  tarifeYap() {
    return { ad: "yurtici", ucret: (g) => 3900 + Math.ceil(g.agirlik) * 900 };
  }
}

class YurtdisiUreteci extends TeklifUreteci {
  tarifeYap() {
    return { ad: "yurtdisi", ucret: (g) => 18400 + Math.ceil(g.agirlik) * 2600 };
  }
}

const G = { agirlik: 2.4, adres: "34100" };
for (const u of [new YurticiUreteci(), new YurtdisiUreteci()]) console.log(u.teklif(G));

class EksikUreteci extends TeklifUreteci {}
try {
  new EksikUreteci().teklif(G);
} catch (e) {
  console.log(`${e.constructor.name}: ${e.message}`);
}
```

```sh
node yontem/kur.mjs
```

```
yurtici: 6600 kurus
yurtdisi: 26200 kurus
TypeError: EksikUreteci tarifeYap yontemini karsilamadi
```

Üst tipin gövdesi hiçbir taşıyıcı adı taşımaz; taşıyıcı adları yalnız alt tiplerin
bulunduğu dosyada geçer. İki dosyada adı içeren satırlar sayıldığında ayrım görünür.

```sh
for d in yontem/uretec.mjs yontem/kur.mjs; do
  echo "$d: $(grep -c 'yurtici\|yurtdisi' $d)"
done
```

```
yontem/uretec.mjs: 0
yontem/kur.mjs: 2
```

Bu ölçü yeni değil: bir önceki derste kayıt sürümü için sayılan "tür adının geçtiği yer"
ölçüsünün aynısıdır ve aynı yönde iyileşir. Fabrika yöntemini kayıttan ayıran şey seçimin
nasıl yapıldığıdır. Kayıtta seçim bir dizgiyle, fabrika yönteminde hangi alt tipin örneğinin
elde tutulduğuyla yapılır. Üçüncü satır kalıbın kendi başına getirdiği tek yeni güvenceyi
gösteriyor: yöntemi karşılamayan bir alt tip ilk çağrıda hata verir, sessiz kalmaz.

## Parçaların Çağrı Yerinde Eşleştirilmesi

Aile problemi fabrika yönteminin çözemediği yerde başlar. Aşağıdaki sürümde üç parça role göre
gruplanmıştır: bütün tarifeler bir dosyada, bütün rotalar bir dosyada, bütün etiketler bir
dosyada. Eşleştirmeyi çağıran yapar.

```js
// rol/tarife.mjs — role gore gruplanmis parcalar: iki ailenin tarifesi bir arada
export const yurticiTarife = { ad: "yurtici", ucret: (g) => 3900 + Math.ceil(g.agirlik) * 900 };
export const yurtdisiTarife = { ad: "yurtdisi", ucret: (g) => 18400 + Math.ceil(g.agirlik) * 2600 };
```

```js
// rol/rota.mjs — iki ailenin rotasi bir arada; gumruk noktasi yalniz yurtdisinda
export const yurticiRota = { duraklar: () => ["34", "06"], gumrukKodu: () => null };
export const yurtdisiRota = { duraklar: () => ["34", "GUM", "DE"], gumrukKodu: () => "TR34DE" };
```

```js
// rol/etiket.mjs — iki ailenin etiketi bir arada; her biri rotadan farkli sey ister
export const yurticiEtiket = (gonderi, rota) => {
  if (rota.gumrukKodu() !== null) throw new TypeError("yurtici etiketi gumruk noktasi tasimaz");
  return `TR ${rota.duraklar().join(">")} ${gonderi.agirlik}kg`;
};

export const yurtdisiEtiket = (gonderi, rota) => {
  const kod = rota.gumrukKodu();
  if (kod === null) throw new TypeError("yurtdisi etiketi gumruk kodu ister");
  return `INT ${kod} ${rota.duraklar().join(">")} ${gonderi.agirlik}kg`;
};
```

```js
// rol/teklif.mjs — uc parcayi cagiran verir; uyumu denetleyen kimse yok
export const teklif = (tarife, rota, etiket, gonderi) =>
  `${tarife.ucret(gonderi)} kurus  ${etiket(gonderi, rota)}`;
```

```js
// rol/kur.mjs — cagiran uc parcayi ada gore kendisi eslestirir
import { yurticiTarife, yurtdisiTarife } from "./tarife.mjs";
import { yurticiRota, yurtdisiRota } from "./rota.mjs";
import { yurticiEtiket, yurtdisiEtiket } from "./etiket.mjs";
import { teklif } from "./teklif.mjs";

export const GONDERI = { agirlik: 2.4, adres: "34100" };
console.log(teklif(yurticiTarife, yurticiRota, yurticiEtiket, GONDERI));
console.log(teklif(yurtdisiTarife, yurtdisiRota, yurtdisiEtiket, GONDERI));
```

```sh
node rol/kur.mjs
```

```
6600 kurus  TR 34>06 2.4kg
26200 kurus  INT TR34DE 34>GUM>DE 2.4kg
```

İki satır doğru. Sorun, `teklif` imzasının doğru olmayan çağrıları da kabul etmesidir: üç
parçanın her biri iki değerden birini alabildiği için sekiz farklı çağrı yazılabilir ve
bunların yalnız ikisi kastedilen çifttir.

## Soyut Fabrika

İkinci sürümde gruplama role göre değil aileye göredir. Her aile bir işlevdir ve çağrıldığında
üç parçayı birlikte döndürür.

```js
// aile/yurtici.mjs — yurtici ailesi: birbirine uyan uc parca birlikte uretilir
export const yurticiAilesi = () => ({
  tarife: { ad: "yurtici", ucret: (g) => 3900 + Math.ceil(g.agirlik) * 900 },
  rota: { duraklar: () => ["34", "06"], gumrukKodu: () => null },
  etiket: (gonderi, rota) => {
    if (rota.gumrukKodu() !== null) throw new TypeError("yurtici etiketi gumruk noktasi tasimaz");
    return `TR ${rota.duraklar().join(">")} ${gonderi.agirlik}kg`;
  },
});
```

```js
// aile/yurtdisi.mjs — yurtdisi ailesi: gumruklu rota ve gumruk kodu isteyen etiket
export const yurtdisiAilesi = () => ({
  tarife: { ad: "yurtdisi", ucret: (g) => 18400 + Math.ceil(g.agirlik) * 2600 },
  rota: { duraklar: () => ["34", "GUM", "DE"], gumrukKodu: () => "TR34DE" },
  etiket: (gonderi, rota) => {
    const kod = rota.gumrukKodu();
    if (kod === null) throw new TypeError("yurtdisi etiketi gumruk kodu ister");
    return `INT ${kod} ${rota.duraklar().join(">")} ${gonderi.agirlik}kg`;
  },
});
```

```js
// aile/teklif.mjs — teklif uc parcayi tek bir aileden alir; eslestirme yapmaz
export const teklif = (aile, gonderi) => {
  const { tarife, rota, etiket } = aile();
  return `${tarife.ucret(gonderi)} kurus  ${etiket(gonderi, rota)}`;
};
```

```js
// aile/kur.mjs — bilesim koku: yalniz aile listesini bilir
import { yurticiAilesi } from "./yurtici.mjs";
import { yurtdisiAilesi } from "./yurtdisi.mjs";
import { teklif } from "./teklif.mjs";

export const AILELER = [yurticiAilesi, yurtdisiAilesi];
export const GONDERI = { agirlik: 2.4, adres: "34100" };
for (const a of AILELER) console.log(teklif(a, GONDERI));
```

```sh
node aile/kur.mjs
```

```
6600 kurus  TR 34>06 2.4kg
26200 kurus  INT TR34DE 34>GUM>DE 2.4kg
```

Aynı iki satır. Fark `teklif` imzasındadır: dört parametre yerine iki parametre alıyor ve
parçalar arası eşleştirme çağıranın elinden çıkmıştır.

## Eşleştirmelerin Sayılması

Aşağıdaki ölçer iki tasarımda yazılabilen bütün çağrıları üretir, her birini çalıştırır ve
sonucu üçe ayırır: kastedilen satırı verenler, hata atanlar ve hata atmadığı hâlde kastedilen
satırı vermeyenler. Üçüncü sınıf, sessizce kabul edilen yanlış çağrılardır.

```js
// birlesim.mjs — iki tasarimda kurulabilen butun eslestirmeleri dener ve siniflar
import { yurticiTarife, yurtdisiTarife } from "./rol/tarife.mjs";
import { yurticiRota, yurtdisiRota } from "./rol/rota.mjs";
import { yurticiEtiket, yurtdisiEtiket } from "./rol/etiket.mjs";
import { teklif as rolTeklif } from "./rol/teklif.mjs";
import { yurticiAilesi } from "./aile/yurtici.mjs";
import { yurtdisiAilesi } from "./aile/yurtdisi.mjs";
import { teklif as aileTeklif } from "./aile/teklif.mjs";

const G = { agirlik: 2.4, adres: "34100" };
const dene = (f) => { try { return f(); } catch { return null; } };
const DOGRU = new Set([aileTeklif(yurticiAilesi, G), aileTeklif(yurtdisiAilesi, G)]);

function say(etiket, satirlar) {
  const hata = satirlar.filter((s) => s === null).length;
  const dogru = satirlar.filter((s) => s !== null && DOGRU.has(s)).length;
  const sessiz = satirlar.length - hata - dogru;
  console.log(`${etiket} birlesim=${satirlar.length} dogru=${dogru} ` +
    `sessiz-yanlis=${sessiz} hata=${hata}`);
}

say("rol ", [yurticiTarife, yurtdisiTarife].flatMap((t) =>
  [yurticiRota, yurtdisiRota].flatMap((r) =>
    [yurticiEtiket, yurtdisiEtiket].map((e) => dene(() => rolTeklif(t, r, e, G))))));
say("aile", [yurticiAilesi, yurtdisiAilesi].map((a) => dene(() => aileTeklif(a, G))));
```

```sh
node birlesim.mjs
```

```
rol  birlesim=8 dogru=2 sessiz-yanlis=2 hata=4
aile birlesim=2 dogru=2 sessiz-yanlis=0 hata=0
```

Birinci sürümde yazılabilen sekiz çağrının dördü hata atıyor, çünkü etiket rotadan gümrük
kodu istiyor ya da gümrük noktası bulunmasını yasaklıyor. Kalan dördün ikisi kastedilen
çifttir; diğer ikisi hata atmaz ve yanlış ücretle geçerli görünen bir etiket üretir. Yurtiçi
tarifesiyle yurtdışı rotası eşleştirildiğinde çıkan satır 6600 kuruş ile bir gümrük etiketi
taşır ve hiçbir denetim bunu yakalamaz.

İkinci sürümde yazılabilen çağrı sayısı sekizden ikiye düşüyor. Kazanç hata yakalamada değil,
yanlış çağrının **yazılamaz** olmasındadır: `teklif` artık parça almadığı için eşleştirme
diye bir işlem kalmamıştır. Soyut fabrikanın ölçülebilir katkısı budur ve sayısı 2'den 0'a
inen sessiz yanlışlardır.

## Yeni Ailenin Maliyeti

İkinci ölçü Tasarım İlkeleri kursundan gelir: yeni bir tür eklendiğinde düzenlenen var olan
dosya ve eklenen satır sayısı. Yeni aile bir hava aktarmalı ekspres taşıyıcıdır; iki ağaç
kopyalanır ve aynı gereksinim ikisine de uygulanır.

```sh
cp -r rol rol-yeni
cp -r aile aile-yeni

cat >> rol-yeni/tarife.mjs <<'SON'
export const ekspresTarife = { ad: "ekspres", ucret: (g) => 24600 + Math.ceil(g.agirlik) * 4100 };
SON
cat >> rol-yeni/rota.mjs <<'SON'
export const ekspresRota = { duraklar: () => ["34", "GUM", "FRA"], gumrukKodu: () => "TR34FR" };
SON
cat >> rol-yeni/etiket.mjs <<'SON'
export const ekspresEtiket = (gonderi, rota) => {
  const kod = rota.gumrukKodu();
  if (kod === null) throw new TypeError("ekspres etiketi gumruk kodu ister");
  return `EXP ${kod} ${rota.duraklar().join(">")} ${gonderi.agirlik}kg`;
};
SON
cat >> rol-yeni/kur.mjs <<'SON'
import { ekspresTarife } from "./tarife.mjs";
import { ekspresRota } from "./rota.mjs";
import { ekspresEtiket } from "./etiket.mjs";
console.log(teklif(ekspresTarife, ekspresRota, ekspresEtiket, GONDERI));
SON

cat > aile-yeni/ekspres.mjs <<'SON'
// aile/ekspres.mjs — ucuncu aile: uc parcasi da bu dosyada
export const ekspresAilesi = () => ({
  tarife: { ad: "ekspres", ucret: (g) => 24600 + Math.ceil(g.agirlik) * 4100 },
  rota: { duraklar: () => ["34", "GUM", "FRA"], gumrukKodu: () => "TR34FR" },
  etiket: (gonderi, rota) => {
    const kod = rota.gumrukKodu();
    if (kod === null) throw new TypeError("ekspres etiketi gumruk kodu ister");
    return `EXP ${kod} ${rota.duraklar().join(">")} ${gonderi.agirlik}kg`;
  },
});
SON
cat >> aile-yeni/kur.mjs <<'SON'
import { ekspresAilesi } from "./ekspres.mjs";
console.log(teklif(ekspresAilesi, GONDERI));
SON

node rol-yeni/kur.mjs
node aile-yeni/kur.mjs

olc() {
  echo "$1 -> $2"
  echo "  duzenlenen var olan dosya = $(diff -rq $1 $2 | grep -c '^Files ')"
  echo "  yeni dosya                = $(diff -rq $1 $2 | grep -c '^Only in ')"
  echo "  eklenen satir             = $(diff -rN $1 $2 | grep '^>' | grep -cvE '^> *(//|$)')"
}
olc rol rol-yeni
olc aile aile-yeni
```

```
6600 kurus  TR 34>06 2.4kg
26200 kurus  INT TR34DE 34>GUM>DE 2.4kg
36900 kurus  EXP TR34FR 34>GUM>FRA 2.4kg
6600 kurus  TR 34>06 2.4kg
26200 kurus  INT TR34DE 34>GUM>DE 2.4kg
36900 kurus  EXP TR34FR 34>GUM>FRA 2.4kg
rol -> rol-yeni
  duzenlenen var olan dosya = 4
  yeni dosya                = 0
  eklenen satir             = 11
aile -> aile-yeni
  duzenlenen var olan dosya = 1
  yeni dosya                = 1
  eklenen satir             = 11
```

Eklenen satır sayısı ikisinde de 11; yazılan kod miktarı aynı. Düzenlenen var olan dosya
sayısı dörtten bire indi ve kalan tek dosya bileşim köküdür. Bu, Bileşen Uyum İlkeleri
dersindeki ayrımın aynısıdır: role göre gruplama bir ailenin parçalarını üç dosyaya dağıtır,
aileye göre gruplama birlikte değişenleri bir arada tutar.

Bedel de sayılabilir. Aile sürümü ekspresi eklerken bir dosya kazandı ve dosya sayısı dörtten
beşe çıktı; ayrıca `etiket` işlevinin gövdesi üç aile dosyasında üç kez ayrı ayrı yazıldı.
Rol sürümünde etiket biçimlerinin üçü aynı dosyada yan yana durur ve etiket sözleşmesi
değişirse tek dosya düzenlenir. Aile sürümünde aynı değişiklik aile sayısı kadar dosyaya
dokunur. Kalıp bir eksene kapanır, öbürüne açılır.

Bu yüzden soyut fabrika, parçalar arasında gerçek bir uyum kısıtı yoksa uygulanmaz. Kısıt
yoksa sessiz yanlış sayısı zaten sıfırdır ve kalıp yalnız dosya sayısını artırır.

## Özet

- Fabrika yöntemi tek bir nesnenin yaratımını bir yönteme çeker; üst tipin gövdesinde tür adı
  geçmez ve yöntemi karşılamayan alt tip ilk çağrıda hata verir.
- Soyut fabrika birbirine uyması gereken parçaları tek çağrıda birlikte üretir; ölçüsü
  yazılabilen çağrı sayısıdır.
- Çağrı yerinde eşleştirme yapılan sürümde sekiz çağrı yazılabiliyordu: dördü hata attı,
  ikisi doğruydu, ikisi hata atmadan yanlış ücret üretti; aile sürümünde çağrı sayısı 2'ye,
  sessiz yanlış sayısı 0'a indi.
- Yeni bir aile eklendiğinde iki sürüm de 11 satır ekledi, ama düzenlenen var olan dosya
  sayısı 4'ten 1'e indi ve o tek dosya bileşim köküdür.
- Kalıbın bedeli etiket sözleşmesi değiştiğinde ortaya çıkar: aile sürümünde bu değişiklik
  aile sayısı kadar dosyaya dokunur, rol sürümünde tek dosyaya.

## Sonraki Adım

Aile işlevi çağrıldığında üç parçayı birlikte döndürüyor, ama parçaların kendisi hâlâ tek
ifadeyle kurulabilen sabitler. Kurulacak nesne çok alanlı olduğunda — gönderinin ağırlığı,
üç boyutu, iki adresi, beyan edilen değeri ve sözleşme numarası varsa — yaratım tek bir
çağrıya sıkıştırılır ve sıralı parametreler birbirinin yerine geçebilir hâle gelir. Sonraki
ders bu çağrıyı adımlara böler: argüman çiftlerinin yer değiştirdiği bütün çağrıları üreterek
sessizce kabul edilen yanlış kurulum sayısını sayar, kurulumu adlandırılmış adımlara taşır ve
zorunlu alan denetimini tek bir noktada toplar.
