---
title: 'Çok Biçimlilik'
source: 'https://academia.sh/tr/kurslar/paradigmalar/cok-bicimlilik'
course: 'Programlama Paradigmaları'
language: tr
updated: '2026-08-17T18:10:42+00:00'
license: 'CC BY-SA 4.0'
---

# Çok Biçimlilik

Alt tip, parametrik ve geçici çok biçimliliğin aynı tarife problemi üzerinde ayrı ayrı gerçekleştirilmesi: sisteme yeni bir tarife türü ve yeni bir işlem eklendiğinde her yolda dokunulan dosya sayısının sayılması ve iki genişleme yönünün karşıtlığının gösterilmesi.

Önceki derste iki alt sınıf aynı adı — `ucret` — farklı gerçekleştirdi ve istemci hangisiyle
çalıştığını bilmedi. Bu, tek bir adın birden çok davranışa karşılık gelmesidir. Alt tip
ilişkisi bunun yollarından yalnız biridir ve seçilen yol, sistemin hangi yönde
genişleyebileceğini belirler.

**Çok biçimlilik** (polymorphism), tek bir arayüzün birden çok tiple çalışabilmesidir.
Programlama Temelleri kursunda tanıtılan tanım budur. Bu derste üç ayrı biçimi aynı problem
üzerinde kurulur ve aralarındaki seçimin bedeli sayılır.

## Üç Ayrı Biçim

**Alt tip çok biçimliliği** (subtype polymorphism), çağrının nesnenin çalışma zamanındaki
tipine göre yönlendirilmesidir. Ad ortak bir üst tipte tanımlıdır, gerçekleştirim alt
tiplerdedir.

**Parametrik çok biçimlilik** (parametric polymorphism), aynı kodun tipten **bağımsız**
çalışmasıdır. Kod hiçbir tipe bakmaz; yaptığı iş tüm tipler için aynıdır. Tipe özgü olan
kısım dışarıdan verilir.

**Geçici çok biçimlilik** (ad hoc polymorphism), her tip için ayrı bir gerçekleştirimin
yazılıp ortak bir adla çağrılmasıdır. Aşırı yükleme destekleyen dillerde bu, aynı adın
farklı imzalarla tanımlanmasıdır; burada tür etiketine bakan bir yönlendirme tablosuyla
gerçekleştirilir.

Problem üçünde de aynıdır: iki tarife türü (`kademeli`, `sabit`) ve iki işlem (`ucret`,
`teslimGunu`).

## Aynı Problem, Üç Gerçekleştirim

Alt tip yolunda üst tip işlemleri bildirir, her tür kendi dosyasında gerçekleştirir.

```js
// v1/alttip/tarife.mjs — ust tip: her tarife turu bu iki yontemi gerceklestirir
export class Tarife {
  ucret(gonderi) { throw new Error(`ucret gerceklestirilmedi: ${gonderi}`); }
  teslimGunu(gonderi) { throw new Error(`teslimGunu gerceklestirilmedi: ${gonderi}`); }
}
```

```js
// v1/alttip/kademeli.mjs — kademeli tarife turu
import { Tarife } from "./tarife.mjs";

export class Kademeli extends Tarife {
  ucret(g) {
    return g.agirlikGram <= 1000 ? 4500 : 4500 + Math.ceil((g.agirlikGram - 1000) / 1000) * 1800;
  }
  teslimGunu(g) { return g.bolge === 1 ? 1 : 3; }
}
```

```js
// v1/alttip/sabit.mjs — sabit ucretli tarife turu
import { Tarife } from "./tarife.mjs";

export class Sabit extends Tarife {
  ucret() { return 9000; }
  teslimGunu() { return 2; }
}
```

Geçici yolda tür bir etikettir ve her **işlem** kendi dosyasında tüm türleri tek tabloda
toplar. Bölünme yönü alt tip yolunun tam tersidir.

```js
// v1/gecici/ucret.mjs — ucret islemi: tum turler icin tek tabloda
const TABLO = {
  kademeli: (g) => (g.agirlikGram <= 1000
    ? 4500 : 4500 + Math.ceil((g.agirlikGram - 1000) / 1000) * 1800),
  sabit: () => 9000,
};

export const ucret = (tarife, gonderi) => TABLO[tarife.tur](gonderi);
```

```js
// v1/gecici/teslim-gunu.mjs — teslim gunu islemi: tum turler icin tek tabloda
const TABLO = { kademeli: (g) => (g.bolge === 1 ? 1 : 3), sabit: () => 2 };

export const teslimGunu = (tarife, gonderi) => TABLO[tarife.tur](gonderi);
```

Parametrik yolda tür hiç geçmez. Aşağıdaki iki işlev, ölçütü dışarıdan aldığı için her
tarife temsiliyle çalışır: sınıf örnekleriyle de, etiketli düz nesnelerle de.

```js
// v1/parametrik/secim.mjs — tur bilgisi kullanmaz; olcutu disaridan alir
export function enUcuz(secenekler, olcut) {
  let en = secenekler[0];
  for (const s of secenekler) if (olcut(s) < olcut(en)) en = s;
  return en;
}

export const siraya = (secenekler, olcut) =>
  [...secenekler].sort((a, b) => olcut(a) - olcut(b));
```

Üç yol aynı gönderi üzerinde aynı sonuçları verir.

```js
// v1/kosum.mjs — uc yolun ayni gonderide ayni sonuclari verdigini gosterir
import { Kademeli } from "./alttip/kademeli.mjs";
import { Sabit } from "./alttip/sabit.mjs";
import { ucret } from "./gecici/ucret.mjs";
import { teslimGunu } from "./gecici/teslim-gunu.mjs";
import { enUcuz } from "./parametrik/secim.mjs";

const G = { agirlikGram: 4000, bolge: 2 };
const nesneler = [new Kademeli(), new Sabit()];
const kayitlar = [{ tur: "kademeli" }, { tur: "sabit" }];

console.log("alttip     ", nesneler.map((t) => `${t.ucret(G)}/${t.teslimGunu(G)}`).join(" "));
console.log("gecici     ", kayitlar.map((t) => `${ucret(t, G)}/${teslimGunu(t, G)}`).join(" "));
console.log("parametrik ", enUcuz(nesneler, (t) => t.ucret(G)).constructor.name,
  enUcuz(kayitlar, (t) => ucret(t, G)).tur);
```

```sh
node v1/kosum.mjs
```

```
alttip      9900/3 9000/2
gecici      9900/3 9000/2
parametrik  Sabit sabit
```

Parametrik satır, o yolun karakterini gösteriyor: `enUcuz` hem sınıf örnekleri hem etiketli
kayıtlar üzerinde çalıştı, çünkü ikisine de bakmadı — yalnız kendisine verilen ölçütü
çağırdı.

## İki Genişleme

Sisteme iki değişiklik gelir. Birincisi yeni bir **tür**: aktarma noktasından geçen gönderi
tarifesi. Aşağıdaki blok ikinci sürümü birinciden kopyalayarak kurar ve bu türü ekler.

```sh
cp -r v1 v2
cat > v2/alttip/aktarmali.mjs <<'DOSYA'
// v2/alttip/aktarmali.mjs — yeni tur: aktarma noktasindan gecen gonderi
import { Tarife } from "./tarife.mjs";

export class Aktarmali extends Tarife {
  ucret(g) { return 6000 + Math.ceil(g.agirlikGram / 1000) * 1200; }
  teslimGunu() { return 5; }
}
DOSYA
cat > v2/gecici/ucret.mjs <<'DOSYA'
// v2/gecici/ucret.mjs — ucret islemi: yeni tur icin satir eklendi
const TABLO = {
  kademeli: (g) => (g.agirlikGram <= 1000
    ? 4500 : 4500 + Math.ceil((g.agirlikGram - 1000) / 1000) * 1800),
  sabit: () => 9000,
  aktarmali: (g) => 6000 + Math.ceil(g.agirlikGram / 1000) * 1200,
};

export const ucret = (tarife, gonderi) => TABLO[tarife.tur](gonderi);
DOSYA
cat > v2/gecici/teslim-gunu.mjs <<'DOSYA'
// v2/gecici/teslim-gunu.mjs — teslim gunu islemi: yeni tur icin satir eklendi
const TABLO = { kademeli: (g) => (g.bolge === 1 ? 1 : 3), sabit: () => 2, aktarmali: () => 5 };

export const teslimGunu = (tarife, gonderi) => TABLO[tarife.tur](gonderi);
DOSYA
echo "v2 kuruldu"
```

```
v2 kuruldu
```

İkinci değişiklik yeni bir **işlem**: gönderinin beyan edilen değerine göre sigorta bedeli.
Üçüncü sürüm ikinciden kopyalanarak kurulur.

```sh
cp -r v2 v3
cat > v3/alttip/tarife.mjs <<'DOSYA'
// v3/alttip/tarife.mjs — ust tipe ucuncu yontem eklendi
export class Tarife {
  ucret(gonderi) { throw new Error(`ucret gerceklestirilmedi: ${gonderi}`); }
  teslimGunu(gonderi) { throw new Error(`teslimGunu gerceklestirilmedi: ${gonderi}`); }
  sigortaBedeli(gonderi) { throw new Error(`sigortaBedeli gerceklestirilmedi: ${gonderi}`); }
}
DOSYA
cat > v3/alttip/kademeli.mjs <<'DOSYA'
// v3/alttip/kademeli.mjs — ucuncu yontem eklendi
import { Tarife } from "./tarife.mjs";

export class Kademeli extends Tarife {
  ucret(g) {
    return g.agirlikGram <= 1000 ? 4500 : 4500 + Math.ceil((g.agirlikGram - 1000) / 1000) * 1800;
  }
  teslimGunu(g) { return g.bolge === 1 ? 1 : 3; }
  sigortaBedeli(g) { return Math.round(g.deger * 0.01); }
}
DOSYA
cat > v3/alttip/sabit.mjs <<'DOSYA'
// v3/alttip/sabit.mjs — ucuncu yontem eklendi
import { Tarife } from "./tarife.mjs";

export class Sabit extends Tarife {
  ucret() { return 9000; }
  teslimGunu() { return 2; }
  sigortaBedeli() { return 0; }
}
DOSYA
cat > v3/alttip/aktarmali.mjs <<'DOSYA'
// v3/alttip/aktarmali.mjs — ucuncu yontem eklendi
import { Tarife } from "./tarife.mjs";

export class Aktarmali extends Tarife {
  ucret(g) { return 6000 + Math.ceil(g.agirlikGram / 1000) * 1200; }
  teslimGunu() { return 5; }
  sigortaBedeli(g) { return Math.round(g.deger * 0.02); }
}
DOSYA
cat > v3/gecici/sigorta-bedeli.mjs <<'DOSYA'
// v3/gecici/sigorta-bedeli.mjs — yeni islem: tek dosya, tum turler
const TABLO = {
  kademeli: (g) => Math.round(g.deger * 0.01),
  sabit: () => 0,
  aktarmali: (g) => Math.round(g.deger * 0.02),
};

export const sigortaBedeli = (tarife, gonderi) => TABLO[tarife.tur](gonderi);
DOSYA
echo "v3 kuruldu"
```

```
v3 kuruldu
```

## Dokunulan Dosya Sayısı

Ölçüm iki sürüm arasındaki farkı sayar. `diff -rq` hem değişen hem yeni eklenen dosya için
bir satır üretir; sayılan bu satırlardır. Karşılaştırma yalnız üç yolun kendi dizinlerini
kapsar — istemci tarafı üç yolda da aynı biçimde etkilendiği için ölçüme girmez.

```sh
for s in alttip gecici parametrik; do
  echo "$s"
  echo "  yeni tur   -> dokunulan dosya = $(diff -rq v1/$s v2/$s | wc -l | tr -d ' ')"
  echo "  yeni islem -> dokunulan dosya = $(diff -rq v2/$s v3/$s | wc -l | tr -d ' ')"
done
```

```
alttip
  yeni tur   -> dokunulan dosya = 1
  yeni islem -> dokunulan dosya = 4
gecici
  yeni tur   -> dokunulan dosya = 2
  yeni islem -> dokunulan dosya = 1
parametrik
  yeni tur   -> dokunulan dosya = 0
  yeni islem -> dokunulan dosya = 0
```

Sayılar karşıt. Alt tip yolunda yeni tür bir dosyaya, yeni işlem dört dosyaya mal oldu.
Geçici yolda ilişki tersine döndü: yeni tür iki dosya, yeni işlem bir dosya. Parametrik
yolda iki genişleme de sıfır dosyaya mal oldu.

Karşıtlığın nedeni bölünme yönüdür. Alt tip yolu kodu **türe göre** böler; bir türün tüm
işlemleri yan yanadır, bu yüzden tür eklemek ucuz, işlem eklemek pahalıdır. Geçici yol kodu
**işleme göre** böler; bir işlemin tüm türleri yan yanadır, bu yüzden ilişki tersine döner.
Bir tasarım iki yönde birden ucuz olamaz; seçim, hangi yönde daha sık genişleneceğine göre
verilir.

Parametrik yolun sıfırları, o yolun bedava olduğu anlamına gelmez. `enUcuz` iki genişlemeden
de etkilenmedi çünkü türe özgü hiçbir iş yapmıyor; ücreti hesaplayan ölçütü çağıran taraf
veriyor. Parametrik çok biçimlilik türe özgü işi ortadan kaldırmaz, onu kendi dışına iter.
Bu yüzden bir sistemde tek başına değil, diğer ikisiyle birlikte bulunur.

Genişletilmiş sistemin üç türü ve üç işlemi aynı sonuçları üretiyor.

```js
// v3/kosum.mjs — genisletilmis sistem: uc tur, uc islem, ayni sonuclar
import { Kademeli } from "./alttip/kademeli.mjs";
import { Sabit } from "./alttip/sabit.mjs";
import { Aktarmali } from "./alttip/aktarmali.mjs";
import { ucret } from "./gecici/ucret.mjs";
import { teslimGunu } from "./gecici/teslim-gunu.mjs";
import { sigortaBedeli } from "./gecici/sigorta-bedeli.mjs";
import { enUcuz } from "./parametrik/secim.mjs";

const G = { agirlikGram: 4000, bolge: 2, deger: 150000 };
const nesneler = [new Kademeli(), new Sabit(), new Aktarmali()];
const kayitlar = [{ tur: "kademeli" }, { tur: "sabit" }, { tur: "aktarmali" }];

const n = (t) => `${t.ucret(G)}/${t.teslimGunu(G)}/${t.sigortaBedeli(G)}`;
const k = (t) => `${ucret(t, G)}/${teslimGunu(t, G)}/${sigortaBedeli(t, G)}`;

console.log("alttip     ", nesneler.map(n).join(" "));
console.log("gecici     ", kayitlar.map(k).join(" "));
console.log("parametrik ", enUcuz(nesneler, (t) => t.ucret(G)).constructor.name,
  enUcuz(kayitlar, (t) => ucret(t, G)).tur);
```

```sh
node v3/kosum.mjs
```

```
alttip      9900/3/1500 9000/2/0 10800/5/3000
gecici      9900/3/1500 9000/2/0 10800/5/3000
parametrik  Sabit sabit
```

## Seçim Ölçütü

Ölçüt, sistemin hangi eksende genişlediğidir ve bu tahminle değil geçmişle yanıtlanır: son
dönemde kaç tür, kaç işlem eklendiğine bakılır.

Tür ekseninin işlek olduğu yerlerde — taşıyıcı seçenekleri, ödeme yöntemleri, bildirim
kanalları — işlem kümesi genelde erken kararlaşır ve alt tip yolu ucuza gelir. İşlem
ekseninin işlek olduğu yerlerde — sabit bir tür kümesi üzerinde sürekli yeni rapor, yeni
doğrulama, yeni dışa aktarma — geçici yol ucuza gelir; tür kümesi kapalıysa yeni bir işlem
eklemek tek dosyaya iner.

Bir de yanlış seçimin sinyali vardır. Alt tip yolunda her yeni işlem için dört dosyaya
dokunuluyorsa ve bu her ay tekrarlanıyorsa, tasarımın yanlış eksende bölündüğü ölçülmüş
demektir. Aynı biçimde, geçici yolda tablolara sürekli yeni tür satırı ekleniyor ve bir tür
eklendiğinde bir tabloyu güncellemek unutuluyorsa — o durumda hata çalışma zamanında
`TABLO[tarife.tur]` tanımsız çıkarak görünür — eksen tersine dönmüş demektir.

## Özet

- Çok biçimliliğin üç biçimi aynı problemi çözebilir: alt tip çalışma zamanı tipine
  yönlendirir, geçici tür etiketine göre tablo seçer, parametrik hiçbir tipe bakmaz.
- Aynı gönderide üç yol da aynı sonucu verdi; karşılaştırma davranış üzerinden değil
  genişleme maliyeti üzerinden yapıldı.
- Yeni bir tür eklendiğinde dokunulan dosya: alt tip 1, geçici 2, parametrik 0.
- Yeni bir işlem eklendiğinde dokunulan dosya: alt tip 4, geçici 1, parametrik 0.
- Karşıtlığın nedeni bölünme yönüdür: alt tip yolu türe göre, geçici yol işleme göre böler;
  bir tasarım iki yönde birden ucuz olamaz.
- Parametrik yolun sıfırları türe özgü işin ortadan kalktığını değil, kodun dışına
  itildiğini gösterir; tek başına değil diğer ikisiyle birlikte kullanılır.

## Sonraki Adım

Alt tip yolundaki `Tarife` sınıfı iki iş yapıyordu: hangi yöntemlerin bulunacağını bildirmek
ve gerçekleştirilmediğinde hata fırlatmak. Bu iki iş ayrılabilir. Bir soyutlama yalnız
sözleşmeyi bildirebilir, bir başkası sözleşmeyle birlikte ortak gerçekleştirimi de
taşıyabilir. Sonraki ders bu iki aracı — arayüz ile soyut sınıfı — karşılaştırır ve seçimin
kırıldığı noktayı koşturarak gösterir: bir tarife türü iki ayrı soyutlamaya birden ihtiyaç
duyduğunda her düzenin ne yaptığı ve kaç satırın tekrar edildiği ölçülür.
