---
title: 'Paradigma Karışımı'
source: 'https://academia.sh/tr/kurslar/paradigmalar/paradigma-karisimi'
course: 'Programlama Paradigmaları'
language: tr
updated: '2026-08-17T18:10:41+00:00'
license: 'CC BY-SA 4.0'
---

# Paradigma Karışımı

Aynı sistemde iki modelin birlikte kullanımı: taşıyıcı seçeneklerinin nesne ağırlıklı ve fonksiyon ağırlıklı iki modeli, yeni bir işlem ile yeni bir türün her iki modelde kaç dosyaya dokunduğunun sayılması ve seçimin ölçüte bağlanması.

Önceki konu bir birim tanıdı: durumu ve onu koruyan davranışı bir arada tutan nesne. Bu konu
başka bir birim kurdu: girdiden çıktıya eşleme yapan, durumu dışarıda bırakan fonksiyon. İki
model aynı alanı — gönderi ücretlendirmeyi — farklı yerlerden böler.

Bu ders ikisini yan yana koyar. Aynı problem iki kez modellenir, sonra iki ayrı değişiklik
istenir ve her birinin iki modelde kaç dosyaya dokunduğu sayılır. Sonuç bir üstünlük ilanı
değildir; hangi ölçüte göre hangisinin seçildiğidir.

## Aynı Problemin İki Modeli

Problem şudur: üç taşıyıcı seçeneği vardır — hızlı, ekonomik ve teslim noktası — ve her biri
üzerinde iki işlem tanımlıdır: ücreti hesaplamak ve teslim süresini tahmin etmek.

Nesne ağırlıklı model, taşıyıcıyı birim alır. Her tür kendi dosyasında durur ve her ikisini
de kendisi bilir.

```sh
mkdir -p nesne
cat > gonderiler.mjs <<'EOF'
export const GONDERILER = [
  { kod: "TR-4471", temelUcret: 4990, mesafeKm: 450, tasiyici: "hizli" },
  { kod: "TR-4472", temelUcret: 6567, mesafeKm: 1200, tasiyici: "ekonomik" },
  { kod: "TR-4473", temelUcret: 14715, mesafeKm: 320, tasiyici: "nokta" },
];
EOF
cat > nesne/hizli.mjs <<'EOF'
export class Hizli {
  ucret(g) { return g.temelUcret + 2500; }
  sure(g) { return Math.ceil(g.mesafeKm / 600) + 1; }
}
EOF
cat > nesne/ekonomik.mjs <<'EOF'
export class Ekonomik {
  ucret(g) { return g.temelUcret; }
  sure(g) { return Math.ceil(g.mesafeKm / 350) + 2; }
}
EOF
cat > nesne/nokta.mjs <<'EOF'
export class Nokta {
  ucret(g) { return g.temelUcret - 750; }
  sure(g) { return Math.ceil(g.mesafeKm / 350) + 3; }
}
EOF
cat > nesne/kayit.mjs <<'EOF'
import { Hizli } from "./hizli.mjs";
import { Ekonomik } from "./ekonomik.mjs";
import { Nokta } from "./nokta.mjs";

export const TASIYICILAR = {
  hizli: new Hizli(), ekonomik: new Ekonomik(), nokta: new Nokta(),
};
EOF
cat > nesne/kullan.mjs <<'EOF'
import { GONDERILER } from "../gonderiler.mjs";
import { TASIYICILAR } from "./kayit.mjs";

for (const g of GONDERILER) {
  const t = TASIYICILAR[g.tasiyici];
  console.log(`${g.kod} ${t.ucret(g)} kurus ${t.sure(g)} gun`);
}
EOF
node nesne/kullan.mjs
```

```
TR-4471 7490 kurus 2 gun
TR-4472 6567 kurus 6 gun
TR-4473 13965 kurus 4 gun
```

Fonksiyon ağırlıklı model işlemi birim alır. Taşıyıcı yalnızca bir addır; her işlem kendi
dosyasında durur ve bütün türleri kendisi bilir.

```sh
mkdir -p fonk
cat > fonk/veri.mjs <<'EOF'
export const TASIYICILAR = ["hizli", "ekonomik", "nokta"];
EOF
cat > fonk/ucret.mjs <<'EOF'
export function ucret(g) {
  switch (g.tasiyici) {
    case "hizli": return g.temelUcret + 2500;
    case "ekonomik": return g.temelUcret;
    case "nokta": return g.temelUcret - 750;
    default: throw new Error(`bilinmeyen tasiyici: ${g.tasiyici}`);
  }
}
EOF
cat > fonk/sure.mjs <<'EOF'
export function sure(g) {
  switch (g.tasiyici) {
    case "hizli": return Math.ceil(g.mesafeKm / 600) + 1;
    case "ekonomik": return Math.ceil(g.mesafeKm / 350) + 2;
    case "nokta": return Math.ceil(g.mesafeKm / 350) + 3;
    default: throw new Error(`bilinmeyen tasiyici: ${g.tasiyici}`);
  }
}
EOF
cat > fonk/kullan.mjs <<'EOF'
import { GONDERILER } from "../gonderiler.mjs";
import { TASIYICILAR } from "./veri.mjs";
import { ucret } from "./ucret.mjs";
import { sure } from "./sure.mjs";

for (const g of GONDERILER) {
  if (TASIYICILAR.includes(g.tasiyici) === false) throw new Error(g.tasiyici);
  console.log(`${g.kod} ${ucret(g)} kurus ${sure(g)} gun`);
}
EOF
node fonk/kullan.mjs
```

```
TR-4471 7490 kurus 2 gun
TR-4472 6567 kurus 6 gun
TR-4473 13965 kurus 4 gun
```

İki model aynı sayıları üretir. Fark davranışta değil, kodun hangi eksende bölündüğündedir.
Nesne modeli türe göre böler: bir dosya bir taşıyıcının her şeyini bilir. Fonksiyon modeli
işleme göre böler: bir dosya bir işlemin her tür için nasıl yapıldığını bilir. Aynı bilgi
kümesi, iki farklı yönde dilimlenmiştir.

## Yeni Bir İşlem Eklendiğinde

Birinci değişiklik yeni bir işlemdir: her gönderi için tahmini karbon salımı, gram cinsinden,
taşıyıcıya bağlı bir katsayıyla hesaplanacak. Ölçüm çağrı yerlerini dışarıda bırakır; yeni bir
işlemin çağrılması her iki modelde de yeni bir çağrı yeri gerektirir.

```sh
cp -r nesne nesne-b && cp -r fonk fonk-b
# Nesne modeli: yeni islem her tur dosyasina ayri ayri eklenir.
cat > nesne-b/hizli.mjs <<'EOF'
export class Hizli {
  ucret(g) { return g.temelUcret + 2500; }
  sure(g) { return Math.ceil(g.mesafeKm / 600) + 1; }
  karbon(g) { return g.mesafeKm * 210; }
}
EOF
cat > nesne-b/ekonomik.mjs <<'EOF'
export class Ekonomik {
  ucret(g) { return g.temelUcret; }
  sure(g) { return Math.ceil(g.mesafeKm / 350) + 2; }
  karbon(g) { return g.mesafeKm * 95; }
}
EOF
cat > nesne-b/nokta.mjs <<'EOF'
export class Nokta {
  ucret(g) { return g.temelUcret - 750; }
  sure(g) { return Math.ceil(g.mesafeKm / 350) + 3; }
  karbon(g) { return g.mesafeKm * 60; }
}
EOF
# Fonksiyon modeli: yeni islem tek bir yeni dosyadir.
cat > fonk-b/karbon.mjs <<'EOF'
export function karbon(g) {
  switch (g.tasiyici) {
    case "hizli": return g.mesafeKm * 210;
    case "ekonomik": return g.mesafeKm * 95;
    case "nokta": return g.mesafeKm * 60;
    default: throw new Error(`bilinmeyen tasiyici: ${g.tasiyici}`);
  }
}
EOF
cat > karbon-kullan.mjs <<'EOF'
import { GONDERILER } from "./gonderiler.mjs";
import { TASIYICILAR } from "./nesne-b/kayit.mjs";
import { karbon } from "./fonk-b/karbon.mjs";

for (const g of GONDERILER) {
  console.log(`${g.kod} nesne ${TASIYICILAR[g.tasiyici].karbon(g)} fonk ${karbon(g)}`);
}
EOF
node karbon-kullan.mjs
for m in nesne fonk; do
  rapor=$(diff -rq -x 'kullan*' "$m" "$m-b")
  printf '%-6s yeni dosya: %d  degisen dosya: %d\n' "$m" \
    "$(printf '%s\n' "$rapor" | grep -c '^Only in')" \
    "$(printf '%s\n' "$rapor" | grep -c '^Files')"
done
```

```
TR-4471 nesne 94500 fonk 94500
TR-4472 nesne 114000 fonk 114000
TR-4473 nesne 19200 fonk 19200
nesne  yeni dosya: 0  degisen dosya: 3
fonk   yeni dosya: 1  degisen dosya: 0
```

İki model de doğru sonucu verir, ama maliyetleri eşit değildir. Fonksiyon modelinde yeni
işlem tek bir yeni dosyadır; var olan hiçbir dosya açılmamıştır. Nesne modelinde üç tür
dosyasının üçü de değişmiştir — yeni bir davranış, o davranışı taşıyacak her sınıfa ayrı ayrı
eklenmek zorundadır.

## Yeni Bir Taşıyıcı Türü Eklendiğinde

İkinci değişiklik ters yöndedir: dördüncü bir taşıyıcı, gece kuryesi, eklenecek. Ücreti sabit
bir ek taşır, teslim süresi taşıdığı mesafeden bağımsız olarak bir gündür.

```sh
cp -r nesne nesne-a && cp -r fonk fonk-a
cat > gonderiler.mjs <<'EOF'
export const GONDERILER = [
  { kod: "TR-4471", temelUcret: 4990, mesafeKm: 450, tasiyici: "hizli" },
  { kod: "TR-4472", temelUcret: 6567, mesafeKm: 1200, tasiyici: "ekonomik" },
  { kod: "TR-4473", temelUcret: 14715, mesafeKm: 320, tasiyici: "nokta" },
  { kod: "TR-4474", temelUcret: 5739, mesafeKm: 280, tasiyici: "gece" },
];
EOF
# Nesne modeli: yeni tur bir yeni dosya, kayit dosyasi guncellenir.
cat > nesne-a/gece.mjs <<'EOF'
export class Gece {
  ucret(g) { return g.temelUcret + 4200; }
  sure(g) { return 1; }
}
EOF
cat > nesne-a/kayit.mjs <<'EOF'
import { Hizli } from "./hizli.mjs";
import { Ekonomik } from "./ekonomik.mjs";
import { Nokta } from "./nokta.mjs";
import { Gece } from "./gece.mjs";

export const TASIYICILAR = {
  hizli: new Hizli(), ekonomik: new Ekonomik(), nokta: new Nokta(), gece: new Gece(),
};
EOF
# Fonksiyon modeli: her islem dosyasina bir dal, veri dosyasina bir ad eklenir.
cat > fonk-a/veri.mjs <<'EOF'
export const TASIYICILAR = ["hizli", "ekonomik", "nokta", "gece"];
EOF
cat > fonk-a/ucret.mjs <<'EOF'
export function ucret(g) {
  switch (g.tasiyici) {
    case "hizli": return g.temelUcret + 2500;
    case "ekonomik": return g.temelUcret;
    case "nokta": return g.temelUcret - 750;
    case "gece": return g.temelUcret + 4200;
    default: throw new Error(`bilinmeyen tasiyici: ${g.tasiyici}`);
  }
}
EOF
cat > fonk-a/sure.mjs <<'EOF'
export function sure(g) {
  switch (g.tasiyici) {
    case "hizli": return Math.ceil(g.mesafeKm / 600) + 1;
    case "ekonomik": return Math.ceil(g.mesafeKm / 350) + 2;
    case "nokta": return Math.ceil(g.mesafeKm / 350) + 3;
    case "gece": return 1;
    default: throw new Error(`bilinmeyen tasiyici: ${g.tasiyici}`);
  }
}
EOF
node nesne-a/kullan.mjs | tail -n 1
node fonk-a/kullan.mjs | tail -n 1
for m in nesne fonk; do
  rapor=$(diff -rq -x 'kullan*' "$m" "$m-a")
  printf '%-6s yeni dosya: %d  degisen dosya: %d\n' "$m" \
    "$(printf '%s\n' "$rapor" | grep -c '^Only in')" \
    "$(printf '%s\n' "$rapor" | grep -c '^Files')"
done
```

```
TR-4474 9939 kurus 1 gun
TR-4474 9939 kurus 1 gun
nesne  yeni dosya: 1  degisen dosya: 1
fonk   yeni dosya: 0  degisen dosya: 3
```

Sayılar tersine dönmüştür. Nesne modelinde yeni tür bir yeni dosyadır; var olan üç tür
dosyasının hiçbiri açılmamış, yalnızca kayıt dosyası güncellenmiştir. Fonksiyon modelinde üç
dosyanın üçü de değişmiştir — her işlem, yeni türü tanımak için kendi dallanmasına bir dal
eklemek zorundadır.

İki ölçüm birlikte okunduğunda tablo tamamlanır. Nesne modeli **yeni türe** kapalıdır ama
yeni işleme açık değildir; fonksiyon modeli **yeni işleme** kapalıdır ama yeni türe açık
değildir. Bir modelin kapalı olduğu eksen, diğerinin açık olduğu eksendir. Bu simetriye
**ifade problemi (expression problem)** denir ve iki paradigmanın ödünleşimini tek cümlede
toplar.

## Seçimin Ölçütü ve Karışım

Ölçüt bu yüzden bir üstünlük yargısı değil, bir tahmindir: sistemin hangi ekseninde büyüme
beklenmektedir. Taşıyıcı sayısı sabit, üzerinde yapılan işlem sayısı artan bir alanda —
ücret, süre, karbon, sigorta, iade — fonksiyon modeli her yeni işlemi tek dosyayla karşılar.
Taşıyıcı sayısı sürekli artan, işlem kümesi oturmuş bir alanda nesne modeli her yeni türü tek
dosyayla karşılar.

İki eksenin de büyüdüğü durumda seçim ikisinden birini almak değil, sınırı çizmektir. Bu
kursun ücretlendirme kitaplığı bu karışımın örneğidir. Ücret hesabının kendisi — kademe
seçimi, bölge çarpanı, indirimler, taban — saf fonksiyonlardan kurulmuş bir boru hattıdır ve
bir sonuç döndürür; girdi/çıktı ile zaman kabukta durur. Taşıyıcı seçenekleri ise nesnedir,
çünkü orada büyüyen eksen tür eksenidir. İki model aynı sistemde, birbirinin yerine değil,
farklı eksenlerde çalışır.

Karışımın tek kuralı sınırın açık olmasıdır. Nesnelerin yaşadığı yerde durum değişebilir;
fonksiyonların yaşadığı yerde veri değiştirilemezdir. Bir değerin bu sınırı hangi yönde
geçtiği belirsizse, iki modelin de güvencesi kaybolur.

## Özet

- Aynı problem iki eksende bölünebilir: nesne modeli türe göre, fonksiyon modeli işleme göre.
  İki model de aynı sayıları üretir.
- Yeni bir işlem eklendiğinde fonksiyon modelinde 1 yeni dosya açılır ve hiçbir dosya
  değişmez; nesne modelinde üç tür dosyasının üçü de değişir.
- Yeni bir tür eklendiğinde nesne modelinde 1 yeni dosya ile 1 kayıt dosyası değişir;
  fonksiyon modelinde üç işlem dosyasının üçü de değişir.
- Bir modelin kapalı olduğu eksen diğerinin açık olduğu eksendir; bu simetri ifade problemi
  olarak adlandırılır.
- Seçim, sistemin hangi ekseninde büyüme beklendiğine bağlıdır; iki eksen de büyüyorsa doğru
  karar birini seçmek değil, iki modelin arasındaki sınırı çizmektir.

## Kurs Kapanışı

Bu kurs bir programın hangi birimlere bölüneceği sorusunu iki ayrı yanıtla ele aldı.

İlk konu yapısal ve nesneye dayalı düzenlemeyi kurdu: denetim akışının disipline edilmesi,
durumun kapsüllenerek değişmezlerin korunması, gerekli olanı gösterip gerisini gizleyen
soyutlama, alt tip ilişkisi olarak kalıtım, üç ayrı çok biçimlilik biçimi, arayüz ile soyut
sınıf arasındaki seçim, kırılgan üst sınıf karşısında bileşimin tercih edilmesi ve veri ile
davranışın birlikte tutulması. İkinci konu fonksiyonel modeli kurdu: saf fonksiyonlar ve
referans saydamlığı, değiştirilemez veriyle paylaşılan durumun ortadan kaldırılması,
davranışın fonksiyon parametresiyle dışarı alınması, küçük adımlardan kurulan boru hatları,
yan etkilerin saf çekirdek ile kirli kabuk arasında bölünmesi ve son olarak iki modelin aynı
sistemde ölçüte bağlı seçimi.

İkisinin ortak bıraktığı bir varsayım vardır. Her iki paradigma da **tek tek birimlerin**
nasıl kurulacağını anlattı — birim nesne mi olacak, fonksiyon mu; içinde ne duracak, dışında
ne kalacak. Kurulan ölçütlerin hepsi birimin içine bakar: kaç karar noktası var, kaç satır
dışarıya dokunuyor, kaç sahte bağımlılık gerekiyor. Birimler arasındaki ilişkinin ne zaman
sağlıklı olduğu ise ölçülmedi. Oysa bir tasarımın iyi olup olmadığı çoğu zaman birimin içine
değil, birimler arasındaki bağa bakılarak anlaşılır: bir birim kaç başkasını tanıyor, bir
değişiklik kaç birime yayılıyor, bir birimin içindeki parçalar gerçekten aynı işe mi ait.

Sonraki kurs, Tasarım İlkeleri, bu soruyu ele alır. SOLID ilkeleri birimlerin sorumluluğunu
ve genişletilme biçimini adlandırır; bağlaşım ve uyum ölçütleri birimler arasındaki bağı ve
birim içindeki bütünlüğü sayılabilir hâle getirir; bağımlılık yönünün soyutlamalara
çevrilmesi, hangi birimin hangisini tanıyacağını bir tercih olmaktan çıkarıp bir kurala
bağlar. Bu kursta kurulan birimler, orada birbirine bağlanacak.
