---
title: 'Arayüz Ayrımı İlkesi'
source: 'https://academia.sh/tr/kurslar/tasarim-ilkeleri/arayuz-ayrimi-ilkesi'
course: 'Tasarım İlkeleri'
language: tr
updated: '2026-08-17T18:10:40+00:00'
license: 'CC BY-SA 4.0'
---

# Arayüz Ayrımı İlkesi

Şişkin bir taşıyıcı arayüzünün istemcilere maliyetinin sayılması: her istemcinin gerçekten çağırdığı yöntem sayısı ile arayüzün dayattığı yöntem sayısının taranarak karşılaştırılması, sahte bağımlılıklardaki toplam yöntem sayısının ölçülmesi ve arayüze dokuzuncu bir yöntem eklendiğinde kaç sahtenin kırıldığının koşumla bulunması.

Sözleşme sınaması iki yöntem üzerinden yürüdü ve her tarife ikisini de gerçekten
kullanıyordu. Taşıyıcı tarafına geçildiğinde bu denge bozulur: taşıyıcı seçimi, rota planlama
ve teslimat takibi aynı nesnenin üstünde toplanır. Tek bir taşıyıcı arayüzü kurulduğunda
yalnız ücret hesaplayan bir istemci de rota ve takip yöntemlerine bağlanır.

**Arayüz ayrımı ilkesi** (interface segregation principle) bunu yasaklar: hiçbir istemci
kullanmadığı yöntemlere bağımlı olmaya zorlanmamalıdır. İlkenin ölçüsü iki ayrı sayının
karşılaştırılmasıdır — istemcinin **çağırdığı** yöntem sayısı ile arayüzün **dayattığı**
yöntem sayısı.

## Tek ve Geniş Sözleşme

İlk sürümde sekiz yöntemin tamamı tek bir sözleşmede toplanmıştır. Sözleşme bir liste ve onu
denetleyen bir doğrulayıcı olarak yazılır; istemciler doğrulayıcıyı çağırarak aldıkları
nesnenin sözleşmeyi karşıladığından emin olur.

```js
// v1/tasiyici.mjs — tek ve genis tasiyici sozlesmesi
export const TASIYICI_SOZLESMESI = [
  "bolgeKatsayisi", "tabanUcret", "aktarmaNoktalari", "tahminiSure",
  "durum", "teslimAlindi", "etiketBas", "iptalEt",
];

export function sozlesmeyiDogrula(t) {
  const eksik = TASIYICI_SOZLESMESI.filter((ad) => typeof t?.[ad] !== "function");
  if (eksik.length > 0) throw new TypeError(`tasiyici sozlesmesi eksik: ${eksik.join(", ")}`);
  return t;
}
```

Üç istemci bu sözleşmeye bağlanır. Her biri `GEREKEN` adıyla, bağlandığı sözleşmeyi dışa
verir; ölçüm betiği bu adı okuyacak.

```js
// v1/ucret-hesabi.mjs — ucret istemcisi: iki yontem cagirir, sekizini ister
import { sozlesmeyiDogrula, TASIYICI_SOZLESMESI } from "./tasiyici.mjs";

export const GEREKEN = TASIYICI_SOZLESMESI;

export function ucret(tasiyici, gonderi) {
  sozlesmeyiDogrula(tasiyici);
  return Math.round((tasiyici.tabanUcret(gonderi) * tasiyici.bolgeKatsayisi(gonderi.adres)) / 100);
}
```

```js
// v1/rota-planlayici.mjs — rota istemcisi
import { sozlesmeyiDogrula, TASIYICI_SOZLESMESI } from "./tasiyici.mjs";

export const GEREKEN = TASIYICI_SOZLESMESI;

export function rota(tasiyici, kaynak, hedef) {
  sozlesmeyiDogrula(tasiyici);
  const noktalar = tasiyici.aktarmaNoktalari(kaynak, hedef);
  return { noktalar, saat: tasiyici.tahminiSure(noktalar) };
}
```

```js
// v1/takip.mjs — takip istemcisi
import { sozlesmeyiDogrula, TASIYICI_SOZLESMESI } from "./tasiyici.mjs";

export const GEREKEN = TASIYICI_SOZLESMESI;

export function teslimEt(tasiyici, takipNo, imza) {
  sozlesmeyiDogrula(tasiyici);
  tasiyici.teslimAlindi(takipNo, imza);
  return tasiyici.durum(takipNo);
}
```

## İki Sayının Ölçülmesi

Ölçüm betiği her istemcinin metninde `tasiyici.` ile başlayan çağrıları tarar ve bulduğu ayrı
yöntem adlarını sayar. Dayatılan sayı ise istemcinin bağlandığı sözleşmenin uzunluğudur.

```js
// arayuz-olc.mjs — istemcinin cagirdigi yontem sayisi ile arayuzun dayattigi sayi
import { readFileSync } from "node:fs";

const CAGRI = /\btasiyici\.(\w+)\(/g;
const kok = process.argv[2];
const istemciler = process.argv.slice(3);
let kullanilanToplam = 0;
let dayatilanToplam = 0;

for (const ad of istemciler) {
  const metin = readFileSync(`${kok}/${ad}`, "utf8");
  const kullanilan = new Set([...metin.matchAll(CAGRI)].map((m) => m[1]));
  const { GEREKEN } = await import(`./${kok}/${ad}`);
  kullanilanToplam += kullanilan.size;
  dayatilanToplam += GEREKEN.length;
  console.log(`${ad.padEnd(20)} kullanilan = ${kullanilan.size}  dayatilan = ${GEREKEN.length}` +
    `  fazlalik = ${GEREKEN.length - kullanilan.size}`);
}
console.log(`${kok}: kullanilan toplam = ${kullanilanToplam}, dayatilan toplam = ${dayatilanToplam}`);
```

```sh
node arayuz-olc.mjs v1 ucret-hesabi.mjs rota-planlayici.mjs takip.mjs
```

```
ucret-hesabi.mjs     kullanilan = 2  dayatilan = 8  fazlalik = 6
rota-planlayici.mjs  kullanilan = 2  dayatilan = 8  fazlalik = 6
takip.mjs            kullanilan = 2  dayatilan = 8  fazlalik = 6
v1: kullanilan toplam = 6, dayatilan toplam = 24
```

Altı çağrının karşılığı yirmi dört bağımlılık. Fazlalık kavramsal değil: her istemci,
kullanmadığı altı yöntemin adına ve varlığına bağlıdır.

## Sahte Bağımlılığın Maliyeti

Fazlalığın ilk somut faturası sahte bağımlılıklarda çıkar. Her istemci için yalnız o
istemcinin çağırdığı yöntemleri taşıyan sahteler yazılır.

```js
// sahte-az.mjs — her sahte yalniz kendi istemcisinin cagirdigi yontemleri tasir
export const SAHTE = {
  ucret: { bolgeKatsayisi: () => 115, tabanUcret: () => 6400 },
  rota: { aktarmaNoktalari: (k, h) => [k, "41", h], tahminiSure: (n) => n.length * 6 },
  takip: { durum: () => "teslim edildi", teslimAlindi: () => undefined },
};
```

```js
// sahte-dene.mjs — verilen sahtelerle uc istemciyi calistirir, kirilani ve maliyeti sayar
const kok = process.argv[2];
const { SAHTE } = await import(process.argv[3]);
const { ucret } = await import(`./${kok}/ucret-hesabi.mjs`);
const { rota } = await import(`./${kok}/rota-planlayici.mjs`);
const { teslimEt } = await import(`./${kok}/takip.mjs`);

const isler = [
  ["ucret", () => ucret(SAHTE.ucret, { agirlik: 3.0, adres: "06500" })],
  ["rota", () => rota(SAHTE.rota, "34", "06")],
  ["takip", () => teslimEt(SAHTE.takip, "TK-1", "A.Y.")],
];

let kirilan = 0;
for (const [ad, f] of isler) {
  try {
    console.log(`${ad.padEnd(6)} -> ${JSON.stringify(f())}`);
  } catch (e) {
    kirilan += 1;
    console.log(`${ad.padEnd(6)} -> ${e.message}`);
  }
}
const yontem = Object.values(SAHTE).reduce((t, s) => t + Object.keys(s).length, 0);
console.log(`${kok} + ${process.argv[3]}: sahte yontem = ${yontem}, kirilan istemci = ${kirilan}`);
```

```sh
node sahte-dene.mjs v1 ./sahte-az.mjs
```

```
ucret  -> tasiyici sozlesmesi eksik: aktarmaNoktalari, tahminiSure, durum, teslimAlindi, etiketBas, iptalEt
rota   -> tasiyici sozlesmesi eksik: bolgeKatsayisi, tabanUcret, durum, teslimAlindi, etiketBas, iptalEt
takip  -> tasiyici sozlesmesi eksik: bolgeKatsayisi, tabanUcret, aktarmaNoktalari, tahminiSure, etiketBas, iptalEt
v1 + ./sahte-az.mjs: sahte yontem = 6, kirilan istemci = 3
```

Üç istemcinin üçü de kırıldı. Çalışması için her sahtenin sekiz yöntemi de taşıması gerekir;
altısı hiç çağrılmayacak dolgudur.

```js
// sahte-dolu.mjs — genis sozlesmeyi karsilamak icin doldurulmus sahteler
const BOS = () => undefined;

export const SAHTE = {
  ucret: {
    bolgeKatsayisi: () => 115, tabanUcret: () => 6400,
    aktarmaNoktalari: BOS, tahminiSure: BOS, durum: BOS, teslimAlindi: BOS,
    etiketBas: BOS, iptalEt: BOS,
  },
  rota: {
    aktarmaNoktalari: (k, h) => [k, "41", h], tahminiSure: (n) => n.length * 6,
    bolgeKatsayisi: BOS, tabanUcret: BOS, durum: BOS, teslimAlindi: BOS,
    etiketBas: BOS, iptalEt: BOS,
  },
  takip: {
    durum: () => "teslim edildi", teslimAlindi: BOS,
    bolgeKatsayisi: BOS, tabanUcret: BOS, aktarmaNoktalari: BOS, tahminiSure: BOS,
    etiketBas: BOS, iptalEt: BOS,
  },
};
```

```sh
node sahte-dene.mjs v1 ./sahte-dolu.mjs
```

```
ucret  -> 7360
rota   -> {"noktalar":["34","41","06"],"saat":18}
takip  -> "teslim edildi"
v1 + ./sahte-dolu.mjs: sahte yontem = 24, kirilan istemci = 0
```

Altı yöntemlik sahte düzeneği yirmi dörde çıktı. Aynı yük gerçek gerçekleştirimlerde de
görülür: yalnız takip hizmeti veren bir taşıyıcı da sekiz yöntemi doldurmak zorundadır.

## Sözleşmenin İstemciye Göre Bölünmesi

İkinci sürümde sekiz yöntem, istemci sayısı kadar sözleşmeye ayrılır. Bölmenin ölçütü
yöntemlerin konusu değil, onları çağıran istemcidir.

```js
// v2/sozlesmeler.mjs — istemciye ozgu uc kucuk sozlesme
export const UCRET_SOZLESMESI = ["bolgeKatsayisi", "tabanUcret"];
export const ROTA_SOZLESMESI = ["aktarmaNoktalari", "tahminiSure"];
export const TAKIP_SOZLESMESI = ["durum", "teslimAlindi"];

export const dogrula = (ad, liste) => (t) => {
  const eksik = liste.filter((y) => typeof t?.[y] !== "function");
  if (eksik.length > 0) throw new TypeError(`${ad} sozlesmesi eksik: ${eksik.join(", ")}`);
  return t;
};
```

```js
// v2/ucret-hesabi.mjs — yalniz ucret sozlesmesine baglanir
import { dogrula, UCRET_SOZLESMESI } from "./sozlesmeler.mjs";

export const GEREKEN = UCRET_SOZLESMESI;
const dogrulaUcret = dogrula("ucret", UCRET_SOZLESMESI);

export function ucret(tasiyici, gonderi) {
  dogrulaUcret(tasiyici);
  return Math.round((tasiyici.tabanUcret(gonderi) * tasiyici.bolgeKatsayisi(gonderi.adres)) / 100);
}
```

```js
// v2/rota-planlayici.mjs — yalniz rota sozlesmesine baglanir
import { dogrula, ROTA_SOZLESMESI } from "./sozlesmeler.mjs";

export const GEREKEN = ROTA_SOZLESMESI;
const dogrulaRota = dogrula("rota", ROTA_SOZLESMESI);

export function rota(tasiyici, kaynak, hedef) {
  dogrulaRota(tasiyici);
  const noktalar = tasiyici.aktarmaNoktalari(kaynak, hedef);
  return { noktalar, saat: tasiyici.tahminiSure(noktalar) };
}
```

```js
// v2/takip.mjs — yalniz takip sozlesmesine baglanir
import { dogrula, TAKIP_SOZLESMESI } from "./sozlesmeler.mjs";

export const GEREKEN = TAKIP_SOZLESMESI;
const dogrulaTakip = dogrula("takip", TAKIP_SOZLESMESI);

export function teslimEt(tasiyici, takipNo, imza) {
  dogrulaTakip(tasiyici);
  tasiyici.teslimAlindi(takipNo, imza);
  return tasiyici.durum(takipNo);
}
```

Sekiz yöntemin tamamını sunan bir taşıyıcı üç sözleşmeyi de karşılar; bölünen arayüz,
gerçekleştirimin bölünmesini gerektirmez. Değişen yalnız istemcinin ne bildiğidir.

```sh
node arayuz-olc.mjs v2 ucret-hesabi.mjs rota-planlayici.mjs takip.mjs
node sahte-dene.mjs v2 ./sahte-az.mjs
```

```
ucret-hesabi.mjs     kullanilan = 2  dayatilan = 2  fazlalik = 0
rota-planlayici.mjs  kullanilan = 2  dayatilan = 2  fazlalik = 0
takip.mjs            kullanilan = 2  dayatilan = 2  fazlalik = 0
v2: kullanilan toplam = 6, dayatilan toplam = 6
ucret  -> 7360
rota   -> {"noktalar":["34","41","06"],"saat":18}
takip  -> "teslim edildi"
v2 + ./sahte-az.mjs: sahte yontem = 6, kirilan istemci = 0
```

Dayatılan toplam yirmi dörtten altıya, sahte düzeneği yirmi dört yöntemden altıya indi.

## Dokuzuncu Yöntem

Asıl fark arayüz büyüdüğünde ortaya çıkar. Sigorta primi hesabı gerekiyor ve yeni yöntem
ücret tarafına aittir. İki ağaç kopyalanır, değişiklik ikisine de uygulanır. Yerinde
düzenlemede yedek uzantısı verilmiştir; GNU ve BSD `sed` bu biçimde aynı davranır.

```sh
cp -r v1 v1-yeni
cp -r v2 v2-yeni
sed -i.y 's/"etiketBas", "iptalEt",/"etiketBas", "iptalEt", "sigortaPrimi",/' v1-yeni/tasiyici.mjs
sed -i.y 's/UCRET_SOZLESMESI = \["bolgeKatsayisi", "tabanUcret"\]/UCRET_SOZLESMESI = ["bolgeKatsayisi", "tabanUcret", "sigortaPrimi"]/' v2-yeni/sozlesmeler.mjs
rm -f v1-yeni/*.y v2-yeni/*.y
node sahte-dene.mjs v1-yeni ./sahte-dolu.mjs
node sahte-dene.mjs v2-yeni ./sahte-az.mjs
```

```
ucret  -> tasiyici sozlesmesi eksik: sigortaPrimi
rota   -> tasiyici sozlesmesi eksik: sigortaPrimi
takip  -> tasiyici sozlesmesi eksik: sigortaPrimi
v1-yeni + ./sahte-dolu.mjs: sahte yontem = 24, kirilan istemci = 3
ucret  -> ucret sozlesmesi eksik: sigortaPrimi
rota   -> {"noktalar":["34","41","06"],"saat":18}
takip  -> "teslim edildi"
v2-yeni + ./sahte-az.mjs: sahte yontem = 6, kirilan istemci = 1
```

Ücret tarafına eklenen tek yöntem, geniş arayüzde üç istemcinin üçünü birden kırdı; bölünmüş
arayüzde yalnız ilgili olanı kırdı. Sigorta priminden hiç haberi olmayan rota planlayıcının
sınaması ilk sürümde bozuldu, ikincisinde bozulmadı.

## Bölmenin Ölçütü ve Sınırı

Arayüzü bölmenin doğru ekseni istemcidir. Yöntemleri konularına göre gruplamak — "hesapla
ilgili olanlar", "kayıtla ilgili olanlar" — aynı sonucu vermez; iki farklı istemci aynı
konudan farklı alt kümeler kullanabilir. Ölçüt tarama sonucudur: hangi istemci hangi yöntem
adlarını çağırıyor.

Bölmenin de bir bedeli vardır. Üç sözleşme üç doğrulayıcı ve üç ad demektir; ayrıca bir
istemci gerçekten dört yöntemin dördünü de çağırıyorsa onu ikiye bölmek fazlalığı
azaltmaz, yalnız dosya sayısını artırır. İlkenin eşiği ölçümdedir: fazlalık sıfırsa bölmenin
kazancı da sıfırdır.

## Özet

- Arayüz ayrımı ilkesi, istemcinin kullanmadığı yöntemlere bağımlı kalmasını yasaklar;
  ölçüsü çağrılan yöntem sayısı ile dayatılan yöntem sayısının farkıdır.
- Geniş sözleşmede üç istemcinin toplam çağrısı 6, dayatılan bağımlılık 24 bulundu; bölünmüş
  sözleşmelerde ikisi de 6 oldu.
- Fazlalığın ilk faturası sahte bağımlılıklardadır: geniş sözleşmeyi karşılamak için sahte
  düzeneği 6 yöntemden 24 yönteme çıktı, 18'i hiç çağrılmayan dolgudur.
- Sözleşmeye dokuzuncu bir yöntem eklendiğinde geniş sürümde 3 istemci, bölünmüş sürümde 1
  istemci kırıldı.
- Bölme ekseni yöntemlerin konusu değil, onları çağıran istemcidir; fazlalık sıfır ölçülen
  yerde bölme yalnız dosya sayısını artırır.

## Sonraki Adım

Bölünmüş sözleşmeler `v2/sozlesmeler.mjs` dosyasında, istemcilerin yanında durdu. Bu yerleşim
tesadüf değil: sözleşmeyi kimin tanımladığı, arayüzün kaç parçaya bölündüğünden ayrı bir
karardır. Sözleşme taşıyıcı gerçekleştiriminin yanında tanımlansaydı, istemciler bölünmüş bir
arayüze bağlanmaya devam ederdi ama yine gerçekleştirimin bulunduğu tarafı ithal ederdi.
Sonraki ders bu iki yerleşimi ithal yönünü çıkararak karşılaştırır ve soyutlamanın hangi
tarafta tanımlandığını bir sayıya çevirir.
