---
title: 'Soyutlamalara Karşı Programlama'
source: 'https://academia.sh/tr/kurslar/tasarim-ilkeleri/soyutlamalara-karsi-programlama'
course: 'Tasarım İlkeleri'
language: tr
updated: '2026-08-17T18:10:40+00:00'
license: 'CC BY-SA 4.0'
---

# Soyutlamalara Karşı Programlama

Somut tipe bağlanmanın maliyetinin sayılması: ikinci bir fiyat listesi eklendiğinde düzenlenen dosya ve dokunulan satır sayısının iki yerleşimde karşılaştırılması, seçim yapan yer sayısının bulunması ve sahte bir fiyat listesinin yalnız soyutlamaya bağlı istemcide etkili olduğunun gösterilmesi.

Bir önceki dersin yalıtılmış yerleşiminde üç istemci de fiyat modülünü adıyla ithal ediyor ve
işlevini doğrudan çağırıyordu. Fiyat listesi tek olduğu sürece bu görünmez. Sözleşmeli
müşteriler için ikinci bir liste geldiğinde soru keskinleşir: istemci hangi listenin
kullanılacağını nereden bilecek?

**Soyutlamalara karşı programlama**, istemcinin somut bir modüle değil, birden çok
gerçekleştirimin karşılayabileceği bir sözleşmeye bağlanmasıdır. Bağımlılığın ters çevrilmesi
dersi sözleşmenin hangi pakette **tanımlandığını** ölçmüştü; buradaki ölçü istemci tarafında:
gerçekleştirim değiştiğinde ya da ikincisi eklendiğinde kaç satıra dokunuluyor?

## Somut Modüle Bağlanan Yerleşim

```sh
mkdir -p somut soyut
```

```js
// kayit.mjs — iki yerlesimin de kullandigi ornek gonderiler
export const KAYITLAR = [
  { kod: "GN-1", agirlik: 0.8, adres: "34100", sozlesmeli: false },
  { kod: "GN-2", agirlik: 3.0, adres: "06500", sozlesmeli: true },
  { kod: "GN-3", agirlik: 12.0, adres: "65200", sozlesmeli: true },
];
```

```js
// somut/fiyat.mjs — standart fiyat listesi
const KADEME = [[1, 3900], [5, 6400], [20, 11800]];
const BOLGE = { "34": 100, "06": 115, "65": 140 };

export function kurusHesabi(gonderi) {
  const taban = KADEME.find(([ust]) => gonderi.agirlik <= ust)?.[1] ?? 15000;
  return Math.round((taban * (BOLGE[gonderi.adres.slice(0, 2)] ?? 160)) / 100);
}
```

```js
// somut/web.mjs — somut fiyat modulunu adiyla ithal eder
import { kurusHesabi } from "./fiyat.mjs";

export const satir = (gonderi) => `${gonderi.kod} ${kurusHesabi(gonderi)} kurus`;
```

```js
// somut/toplu.mjs — somut fiyat modulunu adiyla ithal eder
import { kurusHesabi } from "./fiyat.mjs";

export const satirlar = (gonderiler) =>
  gonderiler.map((g) => `${g.kod} ${kurusHesabi(g)} kurus`);
```

```js
// somut/rapor.mjs — somut fiyat modulunu adiyla ithal eder
import { kurusHesabi } from "./fiyat.mjs";

export const toplamSatiri = (gonderiler) =>
  `TOPLAM ${gonderiler.reduce((t, g) => t + kurusHesabi(g), 0)} kurus`;
```

```js
// somut/kur.mjs — bilesim koku
import { KAYITLAR } from "../kayit.mjs";
import { satir } from "./web.mjs";
import { satirlar } from "./toplu.mjs";
import { toplamSatiri } from "./rapor.mjs";

console.log(satir(KAYITLAR[1]));
console.log(satirlar(KAYITLAR).join(" | "));
console.log(toplamSatiri(KAYITLAR));
```

## Soyutlamaya Bağlanan Yerleşim

İkinci yerleşimde fiyat listesi bir nesnedir ve istemcilere dışarıdan verilir. İstemciler
`kurusHesabi` adında bir yöntemin var olduğunu bilir; hangi listenin geldiğini bilmez.

```js
// soyut/fiyat-standart.mjs — sozlesmeyi karsilayan standart liste
const KADEME = [[1, 3900], [5, 6400], [20, 11800]];
const BOLGE = { "34": 100, "06": 115, "65": 140 };

export const standart = {
  ad: "standart",
  kurusHesabi(gonderi) {
    const taban = KADEME.find(([ust]) => gonderi.agirlik <= ust)?.[1] ?? 15000;
    return Math.round((taban * (BOLGE[gonderi.adres.slice(0, 2)] ?? 160)) / 100);
  },
};
```

```js
// soyut/web.mjs — fiyati disaridan alir, gerceklestirimin adini bilmez
export const satir = (fiyat, gonderi) => `${gonderi.kod} ${fiyat.kurusHesabi(gonderi)} kurus`;
```

```js
// soyut/toplu.mjs — fiyati disaridan alir
export const satirlar = (fiyatSec, gonderiler) =>
  gonderiler.map((g) => `${g.kod} ${fiyatSec(g).kurusHesabi(g)} kurus`);
```

```js
// soyut/rapor.mjs — fiyati disaridan alir
export const toplamSatiri = (fiyatSec, gonderiler) =>
  `TOPLAM ${gonderiler.reduce((t, g) => t + fiyatSec(g).kurusHesabi(g), 0)} kurus`;
```

```js
// soyut/kur.mjs — bilesim koku: hangi fiyat listesinin kullanilacagini yalniz burasi bilir
import { KAYITLAR } from "../kayit.mjs";
import { standart } from "./fiyat-standart.mjs";
import { satir } from "./web.mjs";
import { satirlar } from "./toplu.mjs";
import { toplamSatiri } from "./rapor.mjs";

const fiyatSec = () => standart;

console.log(satir(fiyatSec(KAYITLAR[1]), KAYITLAR[1]));
console.log(satirlar(fiyatSec, KAYITLAR).join(" | "));
console.log(toplamSatiri(fiyatSec, KAYITLAR));
```

```sh
node somut/kur.mjs
node soyut/kur.mjs
```

```
GN-2 7360 kurus
GN-1 3900 kurus | GN-2 7360 kurus | GN-3 16520 kurus
TOPLAM 27780 kurus
GN-2 7360 kurus
GN-1 3900 kurus | GN-2 7360 kurus | GN-3 16520 kurus
TOPLAM 27780 kurus
```

## İkinci Gerçekleştirim

Sözleşmeli müşteri listesi geliyor: kademe ücretleri ve bölge katsayıları farklı. İki ağaç
kopyalanır ve gereksinim ikisine de uygulanır.

```sh
cp -r somut somut-yeni
cp -r soyut soyut-yeni
```

```js
// somut-yeni/fiyat-sozlesmeli.mjs — sozlesmeli musteri listesi
const KADEME = [[1, 3200], [5, 5300], [20, 9700]];
const BOLGE = { "34": 100, "06": 110, "65": 130 };

export function kurusHesabi(gonderi) {
  const taban = KADEME.find(([ust]) => gonderi.agirlik <= ust)?.[1] ?? 13000;
  return Math.round((taban * (BOLGE[gonderi.adres.slice(0, 2)] ?? 150)) / 100);
}
```

```js
// somut-yeni/web.mjs — iki somut modulu de ithal eder ve arasinda secim yapar
import { kurusHesabi } from "./fiyat.mjs";
import { kurusHesabi as sozlesmeliHesabi } from "./fiyat-sozlesmeli.mjs";

export const satir = (gonderi) =>
  `${gonderi.kod} ${gonderi.sozlesmeli ? sozlesmeliHesabi(gonderi) : kurusHesabi(gonderi)} kurus`;
```

```js
// somut-yeni/toplu.mjs — ayni secim ikinci kez
import { kurusHesabi } from "./fiyat.mjs";
import { kurusHesabi as sozlesmeliHesabi } from "./fiyat-sozlesmeli.mjs";

export const satirlar = (gonderiler) =>
  gonderiler.map((g) =>
    `${g.kod} ${g.sozlesmeli ? sozlesmeliHesabi(g) : kurusHesabi(g)} kurus`);
```

```js
// somut-yeni/rapor.mjs — ayni secim ucuncu kez
import { kurusHesabi } from "./fiyat.mjs";
import { kurusHesabi as sozlesmeliHesabi } from "./fiyat-sozlesmeli.mjs";

export const toplamSatiri = (gonderiler) =>
  `TOPLAM ${gonderiler.reduce((t, g) =>
    t + (g.sozlesmeli ? sozlesmeliHesabi(g) : kurusHesabi(g)), 0)} kurus`;
```

Soyut yerleşimde istemciler açılmaz. Yeni liste aynı sözleşmeyi karşılayan ikinci bir nesnedir
ve seçim bileşim kökünde yapılır.

```js
// soyut-yeni/fiyat-sozlesmeli.mjs — ayni sozlesmeyi karsilayan ikinci liste
const KADEME = [[1, 3200], [5, 5300], [20, 9700]];
const BOLGE = { "34": 100, "06": 110, "65": 130 };

export const sozlesmeli = {
  ad: "sozlesmeli",
  kurusHesabi(gonderi) {
    const taban = KADEME.find(([ust]) => gonderi.agirlik <= ust)?.[1] ?? 13000;
    return Math.round((taban * (BOLGE[gonderi.adres.slice(0, 2)] ?? 150)) / 100);
  },
};
```

```js
// soyut-yeni/kur.mjs — bilesim koku: hangi fiyat listesinin kullanilacagini yalniz burasi bilir
import { KAYITLAR } from "../kayit.mjs";
import { standart } from "./fiyat-standart.mjs";
import { sozlesmeli } from "./fiyat-sozlesmeli.mjs";
import { satir } from "./web.mjs";
import { satirlar } from "./toplu.mjs";
import { toplamSatiri } from "./rapor.mjs";

const fiyatSec = (g) => (g.sozlesmeli ? sozlesmeli : standart);

console.log(satir(fiyatSec(KAYITLAR[1]), KAYITLAR[1]));
console.log(satirlar(fiyatSec, KAYITLAR).join(" | "));
console.log(toplamSatiri(fiyatSec, KAYITLAR));
```

## Bedelin Sayılması

```sh
node somut-yeni/kur.mjs
node soyut-yeni/kur.mjs
for k in somut soyut; do
  echo "$k: duzenlenen dosya = $(diff -rq $k $k-yeni | grep -c '^Files')" \
    " yeni dosya = $(diff -rq $k $k-yeni | grep -c '^Only in')" \
    " dokunulan satir = $(diff -rU0 $k $k-yeni | grep -cE '^[+-][^+-]')" \
    " secim yapan dosya = $(grep -l 'sozlesmeli ?' $k-yeni/*.mjs | wc -l | tr -d ' ')"
done
```

```
GN-2 5830 kurus
GN-1 3900 kurus | GN-2 5830 kurus | GN-3 12610 kurus
TOPLAM 22340 kurus
GN-2 5830 kurus
GN-1 3900 kurus | GN-2 5830 kurus | GN-3 12610 kurus
TOPLAM 22340 kurus
somut: duzenlenen dosya = 3  yeni dosya = 1  dokunulan satir = 18  secim yapan dosya = 3
soyut: duzenlenen dosya = 1  yeni dosya = 1  dokunulan satir = 5  secim yapan dosya = 1
```

Aynı gereksinim, aynı sonuç, farklı maliyet: on sekiz satıra karşı beş satır, üç düzenlenen
dosyaya karşı bir. Son sütun farkın nedenini veriyor — somut yerleşimde seçim üç ayrı yerde
yapılıyor. Üçüncü bir liste eklendiğinde bu üç yerin üçü de yeniden düzenlenecek; soyut
yerleşimde düzenlenecek yer bileşim kökü olarak kalacak.

## Yerine Koyabilme

İkinci ölçü, gerçekleştirimin sınama sırasında değiştirilebilmesidir. Aynı istemciye sabit
değer üreten sahte bir fiyat listesi verilir.

```js
// sahte-dene.mjs — istemciler sahte bir fiyat listesiyle calisabiliyor mu
const SAHTE = { ad: "sahte", kurusHesabi: () => 1000 };
const G = { kod: "GN-9", agirlik: 3.0, adres: "06500", sozlesmeli: false };

const somutWeb = await import("./somut/web.mjs");
const soyutWeb = await import("./soyut/web.mjs");

console.log("somut ->", somutWeb.satir(G));
console.log("soyut ->", soyutWeb.satir(SAHTE, G));
```

```sh
node sahte-dene.mjs
```

```
somut -> GN-9 7360 kurus
soyut -> GN-9 1000 kurus
```

Somut yerleşimde sahte listenin verilecek bir yeri yok; istemci onu görmezden geldi ve gerçek
fiyatı hesapladı. Soyut yerleşimde aynı istemci sahteyi kullandı. Fark bir kolaylık değil,
yapısal: somut ithal bir seçim noktası bırakmaz.

## İlkenin Bedeli

Soyutlama karşılıksız değildir. Soyut yerleşimde üç istemcinin imzası bir parametre uzadı ve
`fiyatSec` işlevi bileşim kökünde ek bir dolaylılık kurdu. Gerçekleştirim tek kalacaksa bu
fazlalık geri kazanılmaz: ölçüm, ikinci bir gerçekleştirim geldiğinde kazanç verdi, hiç
gelmeseydi yalnız parametre sayısını artırmış olurdu.

Ölçüt bu yüzden gerçekleştirim sayısının geçmişidir. Bir modülün ikinci bir gerçekleştirimi
hiç istenmediyse ve sınamada da değiştirilmesi gerekmiyorsa somut ithal doğru seçimdir.
Sınamada değiştirilmesi gerekiyorsa gerçekleştirim sayısı zaten ikidir; sahte de bir
gerçekleştirimdir.

## Özet

- Soyutlamalara karşı programlama, istemcinin somut bir modüle değil, birden çok
  gerçekleştirimin karşılayabileceği bir sözleşmeye bağlanmasıdır.
- İkinci fiyat listesi eklendiğinde somut yerleşimde 3 dosya ve 18 satır, soyut yerleşimde
  1 dosya ve 5 satır dokunuldu.
- Farkın kaynağı seçim noktasıdır: somut yerleşimde seçim 3 dosyada, soyut yerleşimde 1
  dosyada yapılıyor.
- Sahte bir fiyat listesi yalnız soyut yerleşimde etkili oldu; somut ithal, gerçekleştirimin
  değiştirileceği bir nokta bırakmıyor.
- Soyutlamanın bedeli parametre sayısı ve bir dolaylılık katmanıdır; ikinci gerçekleştirim
  hiç gelmeyecekse bu bedel geri kazanılmaz.

## Sonraki Adım

Soyut yerleşimde bileşim kökü hangi fiyat listesinin kullanılacağına karar veriyor, ama
istemcileri hâlâ kendisi çağırıyor: önce `satir`, sonra `satirlar`, sonra `toplamSatiri`.
Adımların sırası ve sayısı çağıranın gövdesinde duruyor. Bu akışa yeni bir adım eklemek —
sözleşmeli müşteri indirimi, sigorta primi, bir denetim satırı — çağıranı her seferinde
düzenlemeyi gerektirir. Sonraki ders çağıran ile çağrılan arasındaki yönü tersine çevirir:
adımların sırasını bir iskelete, adımların kendisini iskelete verilen parçalara taşır ve yeni
bir adım eklemenin maliyetini iki düzende karşılaştırır.
