---
title: 'Arayüzler ve Soyut Sınıflar'
source: 'https://academia.sh/tr/kurslar/paradigmalar/arayuzler-ve-soyut-siniflar'
course: 'Programlama Paradigmaları'
language: tr
updated: '2026-08-17T18:10:42+00:00'
license: 'CC BY-SA 4.0'
---

# Arayüzler ve Soyut Sınıflar

İki soyutlama aracının seçim ölçütü: ortak davranışın soyut sınıfla ve arayüzle paylaşıldığı iki düzenin kurulması, bir taşıyıcı iki soyutlamaya birden ihtiyaç duyduğunda tek kalıtımın kırıldığının koşturularak gösterilmesi ve tekrar edilen satır sayısının sayılması.

Önceki dersteki `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. İki araç arasındaki seçim buradan doğar.

**Arayüz** (interface), bir tipin hangi yöntemleri sunacağını bildiren, hiçbir gerçekleştirim
taşımayan sözleşmedir; TypeScript kursunda tanıtılan biçim budur. **Soyut sınıf** (abstract
class) ise sözleşmenin yanında ortak gerçekleştirimi de taşır ve alt sınıflara devreder.
Aradaki fark tek satırla özetlenir: bir tip birçok arayüz gerçekleştirebilir, ama yalnız bir
sınıftan türeyebilir.

## Ortak Davranışı Soyut Sınıfla Paylaşmak

Ücretlendirme kitaplığında taşıyıcılar iki ayrı yetenek taşır. Birincisi ücretlendirilebilir
olmaktır: taşıyıcı gönderi başına birim ücreti bilir, toplamı asgari ücretle sınırlayan
hesap ise ortaktır.

```js
// soyut/ucretli.mjs — soyut sinif: ortak toplam hesabini tasir, birim ucreti alt sinifa birakir
export const ASGARI = 5000;

export class UcretliTasiyici {
  birimUcret(gonderi) { throw new Error(`birimUcret gerceklestirilmedi: ${gonderi.kod}`); }
  toplamUcret(gonderiler) {
    return Math.max(gonderiler.reduce((t, g) => t + this.birimUcret(g), 0), ASGARI);
  }
}
```

İkincisi izlenebilir olmaktır: taşıyıcı bir takip kodunun durum kaydını verir, kayıtlar
arasından en yenisini seçen mantık ise yine ortaktır.

```js
// soyut/izlenebilir.mjs — soyut sinif: son durumu secen ortak mantigi tasir
export class IzlenebilirTasiyici {
  durumKaydi(kod) { throw new Error(`durumKaydi gerceklestirilmedi: ${kod}`); }
  sonDurum(kodlar) {
    const kayitlar = kodlar.map((k) => this.durumKaydi(k)).filter((k) => k !== null);
    return kayitlar.reduce((en, k) => (k.zaman > en.zaman ? k : en), kayitlar[0]);
  }
}
```

Aktarmalı taşıyıcı ikisine birden ihtiyaç duyar: hem ücretlendirilir hem izlenir. Doğrudan
karşılık, iki soyut sınıftan birden türemektir.

```js
// soyut/aktarmali-hatali.mjs — iki soyut siniftan birden turetme denemesi
import { UcretliTasiyici } from "./ucretli.mjs";
import { IzlenebilirTasiyici } from "./izlenebilir.mjs";

export class AktarmaliTasiyici extends UcretliTasiyici, IzlenebilirTasiyici {
  birimUcret(g) { return 6000 + Math.ceil(g.agirlikGram / 1000) * 1200; }
  durumKaydi(kod) { return { kod, durum: "aktarmada", zaman: kod.length }; }
}
```

Aşağıdaki komut bu dosyayı çalıştırır. Çıktının dosya yolunu, yığıt izini ve sürüm satırını
içeren bölümleri ortama bağlı olduğu için ayıklanmıştır; kalan üç satır hatanın kendisidir.

```sh
node soyut/aktarmali-hatali.mjs 2>&1 | sed -n '2,3p;5p'
```

```
export class AktarmaliTasiyici extends UcretliTasiyici, IzlenebilirTasiyici {
                                                      ^
SyntaxError: Unexpected token ','
```

Hata çalışma zamanında değil, dosya derlenirken çıkıyor. Tek kalıtım bir kütüphane kısıtı
değil, dilin dilbilgisine yazılmış bir kısıttır. Aynı kısıt tek kalıtımlı her dilde geçerlidir
ve ortak gerçekleştirimin sınıf hiyerarşisiyle paylaşıldığı her tasarımın üst sınırını
belirler: **bir tip ortak gerçekleştirimi en çok bir kaynaktan devralabilir.**

## Çıkış Yolu ve Bedeli

Kısıtın önündeki yollardan biri, bir soyut sınıftan türeyip ötekinin ortak kodunu kopyalamaktır.

```js
// soyut/aktarmali.mjs — cikis yolu: bir soyut siniftan turetilir, otekinin ortak kodu kopyalanir
import { UcretliTasiyici } from "./ucretli.mjs";

export class AktarmaliTasiyici extends UcretliTasiyici {
  birimUcret(g) { return 6000 + Math.ceil(g.agirlikGram / 1000) * 1200; }
  durumKaydi(kod) { return { kod, durum: "aktarmada", zaman: kod.length }; }
  sonDurum(kodlar) {
    const kayitlar = kodlar.map((k) => this.durumKaydi(k)).filter((k) => k !== null);
    return kayitlar.reduce((en, k) => (k.zaman > en.zaman ? k : en), kayitlar[0]);
  }
}

export class YarimTasiyici extends UcretliTasiyici {
  durumKaydi(kod) { return { kod, durum: "beklemede", zaman: 0 }; }
}
```

İkinci bir yol, iki soyut sınıfı zincirlemektir: `IzlenebilirTasiyici` sınıfını
`UcretliTasiyici` sınıfından türetmek. Bu, kısıtı aşar ama izlenen her taşıyıcıyı
ücretlendirilebilir yapar; ücretlendirilmeyen bir aktarma noktası izleyicisi de ücret
yöntemlerini devralır. Önceki dersin ölçütüyle bu, sözleşmesi tutmayan bir alt tip üretir.

## Ortak Davranışı Arayüzle Paylaşmak

İkinci düzende soyutlama yalnız sözleşme bildirir. Sözleşme, gereken yöntem adlarının
listesidir; uyum yapısal olarak denetlenir.

```js
// arayuz/sozlesme.mjs — arayuzler yalnizca gereken yontem adlarini bildirir
export const UCRETLENDIRILEBILIR = ["birimUcret"];
export const IZLENEBILIR = ["durumKaydi"];

export const eksikler = (nesne, ...sozlesmeler) =>
  sozlesmeler.flat().filter((ad) => typeof nesne[ad] !== "function");
```

Ortak davranış kalıtımla değil, taşıyıcıyı argüman alan işlevlerle paylaşılır. Bu işlevler
hiçbir sınıfa bağlı değildir; sözleşmeyi karşılayan her nesneyle çalışır.

```js
// arayuz/ortak.mjs — ortak davranis kalitimla degil, tasiyiciyi argüman alan islevlerle paylasilir
export const ASGARI = 5000;

export const toplamUcret = (tasiyici, gonderiler) =>
  Math.max(gonderiler.reduce((t, g) => t + tasiyici.birimUcret(g), 0), ASGARI);

export function sonDurum(tasiyici, kodlar) {
  const kayitlar = kodlar.map((k) => tasiyici.durumKaydi(k)).filter((k) => k !== null);
  return kayitlar.reduce((en, k) => (k.zaman > en.zaman ? k : en), kayitlar[0]);
}
```

Aktarmalı taşıyıcı iki sözleşmeyi de karşılar ve hiçbir ortak kodu kopyalamaz.

```js
// arayuz/aktarmali.mjs — iki arayuzu birden gerceklestirir; ortak kod kopyalanmaz
export class AktarmaliTasiyici {
  birimUcret(g) { return 6000 + Math.ceil(g.agirlikGram / 1000) * 1200; }
  durumKaydi(kod) { return { kod, durum: "aktarmada", zaman: kod.length }; }
}

export class YarimTasiyici {
  durumKaydi(kod) { return { kod, durum: "beklemede", zaman: 0 }; }
}
```

## Tekrar Edilen Satır

Ölçüm, somut taşıyıcı dosyasındaki satırlardan kaçının ortak davranış dosyasında birebir
bulunduğunu sayar; yorum, `import` satırları ve çok kısa satırlar sayıma girmez.

```js
// tekrar.mjs — iki duzende tekrar edilen ortak davranis satirlarini sayar
import { readFileSync } from "node:fs";

const satirlar = (yol) => readFileSync(yol, "utf8").split("\n")
  .map((s) => s.trim())
  .filter((s) => s.length > 3 && !s.startsWith("//") && !s.startsWith("import"));

const tekrar = (kaynak, ortak) => {
  const kume = new Set(satirlar(ortak));
  return satirlar(kaynak).filter((s) => kume.has(s)).length;
};

console.log(`soyut sinif duzeni  tekrar edilen satir = ${tekrar("soyut/aktarmali.mjs", "soyut/izlenebilir.mjs")}`);
console.log(`arayuz duzeni       tekrar edilen satir = ${tekrar("arayuz/aktarmali.mjs", "arayuz/ortak.mjs")}`);
```

```sh
node tekrar.mjs
```

```
soyut sinif duzeni  tekrar edilen satir = 3
arayuz duzeni       tekrar edilen satir = 0
```

Üç satır küçük bir sayı gibi görünür, ama sabit değildir. İki yeteneğe birden ihtiyaç duyan
her taşıyıcı aynı üç satırı kopyalar; on taşıyıcılı bir filoda otuz satır olur ve son durum
seçme kuralı değiştiğinde on dosyaya dokunulur. Arayüz düzeninde aynı değişiklik tek
dosyadadır, çünkü ortak kod hiç kopyalanmamıştır.

## İki Düzen Aynı Sonucu Veriyor

Karşılaştırmanın anlamlı olması için iki düzenin aynı işi yapması gerekir. Aynı koşum,
eksik gerçekleştirimin ne zaman görüldüğünü de ölçer: her iki filoda üçüncü taşıyıcı
`birimUcret` yöntemini gerçekleştirmemiştir.

```js
// kosum.mjs — iki duzenin ayni sonucu vermesi ve eksik gerceklestirimin ne zaman gorulmesi
import { AktarmaliTasiyici as SoyutTam, YarimTasiyici as SoyutYarim } from "./soyut/aktarmali.mjs";
import { AktarmaliTasiyici as ArayuzTam, YarimTasiyici as ArayuzYarim } from "./arayuz/aktarmali.mjs";
import { toplamUcret, sonDurum } from "./arayuz/ortak.mjs";
import { eksikler, UCRETLENDIRILEBILIR, IZLENEBILIR } from "./arayuz/sozlesme.mjs";

const GONDERILER = [{ kod: "TR1", agirlikGram: 4000 }, { kod: "TR2", agirlikGram: 900 }];
const KODLAR = ["TR1", "TR2200", "TR30"];

const s = new SoyutTam();
const a = new ArayuzTam();
console.log(`soyut  toplam = ${s.toplamUcret(GONDERILER)}  son durum = ${s.sonDurum(KODLAR).kod}`);
console.log(`arayuz toplam = ${toplamUcret(a, GONDERILER)}  son durum = ${sonDurum(a, KODLAR).kod}`);

const soyutFilo = [new SoyutTam(), new SoyutTam(), new SoyutYarim()];
let islenen = 0;
let hata = "";
try {
  for (const t of soyutFilo) { t.toplamUcret(GONDERILER); islenen += 1; }
} catch (e) { hata = e.message; }
console.log(`soyut  hatadan once islenen = ${islenen}/3  hata: ${hata}`);

const arayuzFilo = [new ArayuzTam(), new ArayuzTam(), new ArayuzYarim()];
const eksik = arayuzFilo.map((t) => eksikler(t, UCRETLENDIRILEBILIR, IZLENEBILIR));
const uygun = arayuzFilo.filter((_, i) => eksik[i].length === 0);
for (const t of uygun) toplamUcret(t, GONDERILER);
console.log(`arayuz kullanimdan once reddedilen = ${eksik.filter((e) => e.length > 0).length}/3` +
  `  eksik yontem: ${eksik.flat().join(",")}`);
console.log(`arayuz calisma zamani hatasi = 0  islenen = ${uygun.length}/3`);
```

```sh
node kosum.mjs
```

```
soyut  toplam = 18000  son durum = TR2200
arayuz toplam = 18000  son durum = TR2200
soyut  hatadan once islenen = 2/3  hata: birimUcret gerceklestirilmedi: TR1
arayuz kullanimdan once reddedilen = 1/3  eksik yontem: birimUcret
arayuz calisma zamani hatasi = 0  islenen = 2/3
```

İki düzen aynı toplamı ve aynı son durumu verdi. Eksik gerçekleştirimde ayrıldılar: soyut
sınıf düzeninde hata ancak üçüncü taşıyıcı işlenmeye başladığında çıktı ve o noktada iki
taşıyıcı zaten işlenmişti. Arayüz düzeninde eksiklik hiçbir iş yapılmadan önce ada kadar
bildirildi.

Bu fark aracın kendisinden değil, denetimin nerede yapıldığından gelir. Soyut sınıfın
fırlatan yöntemi bir çağrı bekler; sözleşme listesi ise çağrı gerektirmez. Statik tipleme
kullanılan bir ortamda aynı denetim derleme zamanına iner ve koşumda hiç görünmez —
TypeScript kursunda kurulan yapısal tipleme tam olarak bunu yapar.

## Seçim Ölçütü

Soyut sınıf, ortak gerçekleştirim gerçekten paylaşılıyorsa ve bir tipin tek bir aileye ait
olduğu kesinse tercih edilir; kalıtım hem sözleşmeyi hem kodu tek adımda taşır. Arayüz,
bir tipin birden çok yetenek taşıması bekleniyorsa tercih edilir; ölçülen 3'e karşı 0
satırlık fark ve bunun taşıyıcı sayısıyla çarpılması bu seçimin bedelidir.

İki araç birbirini dışlamaz. Yaygın düzen, sözleşmeyi arayüzle bildirmek ve ortak
gerçekleştirimi bağımsız işlevlerde ya da tek bir yardımcı tipte toplamaktır; böylece
paylaşım kalıtım hattını tüketmez. Bu düzenin adı bileşimdir ve kalıtımla arasındaki seçim
sonraki dersin konusudur.

## Özet

- Arayüz yalnız sözleşme bildirir, soyut sınıf sözleşmeyle birlikte ortak gerçekleştirimi de
  taşır; bir tip birçok arayüz gerçekleştirebilir ama tek bir sınıftan türeyebilir.
- İki soyut sınıftan birden türeme denemesi çalışma zamanına bile ulaşmadan `SyntaxError`
  ile durdu; tek kalıtım dilin dilbilgisine yazılmış bir kısıttır.
- Kısıtın çıkış yolu ortak kodun kopyalanmasıdır: soyut sınıf düzeninde 3 satır tekrar
  edildi, arayüz düzeninde 0.
- Tekrar sabit değildir; iki yeteneğe birden ihtiyaç duyan her taşıyıcı aynı üç satırı
  çoğaltır ve kural değiştiğinde dokunulan dosya sayısı taşıyıcı sayısı kadar olur.
- İki düzen aynı toplamı ve aynı son durumu üretti; ayrıldıkları yer eksik gerçekleştirimin
  görülme anıydı: soyut sınıfta iki taşıyıcı işlendikten sonra, arayüzde hiç iş yapılmadan.
- İki soyut sınıfı zincirlemek kısıtı aşar ama izlenen her taşıyıcıyı ücretlendirilebilir
  yaparak sözleşmesi tutmayan bir alt tip üretir.

## Sonraki Adım

Bu derste ortak davranış iki yolla paylaşıldı: kalıtımla ve taşıyıcıyı argüman alan
işlevlerle. İkincisi kalıtım hattını tüketmediği için iki yeteneği bir arada taşıyabildi.
Aynı tercihin bir başka sonucu daha vardır ve o, üst sınıfın kendi içinde bir değişiklik
yaptığında ortaya çıkar. Sonraki ders bunu ölçer: bir üst sınıfta bir yöntemin
gerçekleştirimi değiştiğinde alt sınıfın davranışının sessizce değişmesi bir koşumla
gösterilir, sonra aynı değişiklik bileşimli sürümde denenir.
