---
title: 'Bileşen Uyum İlkeleri'
source: 'https://academia.sh/tr/kurslar/tasarim-ilkeleri/bilesen-uyum-ilkeleri'
course: 'Tasarım İlkeleri'
language: tr
updated: '2026-08-17T18:10:39+00:00'
license: 'CC BY-SA 4.0'
---

# Bileşen Uyum İlkeleri

Bileşen sınırının birlikte değişen dosyalardan çıkarılması: değişiklik günlüğünden birlikte değişim sayısının hesaplanması, dosyaların eşikle kümelenmesi ve dizin bölünmesi ile birlikte-değişim bölünmesinin bileşen sayısı, yeniden yayın sayısı ve bağlanılan dosya sayısıyla karşılaştırılması.

Buraya kadarki üç ders sınırı tek tek dosyalar üzerinde tartıştı: politika hangi dosyada
duruyor, iki modül arasındaki sözleşme kaç addan oluşuyor, dış aracın sözlüğüne kaç dosya
dokunuyor. Bu ölçeklerde birim dosyadır ve sınır bir ithal satırıdır.

Kod tabanı büyüdüğünde birim değişir. Dosyalar tek tek değil, kümeler hâlinde paketlenir,
sürümlenir ve yayınlanır; bu kümeye **bileşen** (component) denir. Yeni soru şudur: hangi
dosyalar aynı bileşene girmelidir? Bu ders yanıtı tahminden değil, kitaplığın değişiklik
günlüğünden çıkarır.

## Bileşen Yeniden Kullanım Birimidir

Bir bileşenin ilk ölçütü teknik değil, yayın ile ilgilidir: bir kitaplığı kullanan taraf onu
dosya dosya değil, sürüm sürüm alır. Bu yüzden **yeniden kullanım birimi yayın birimine
eşittir** — yeniden kullanım–yayın eşdeğerliği ilkesi (reuse/release equivalence principle)
budur. Ayrı sürümlenemeyen bir küme yeniden kullanılamaz, çünkü kullanan tarafın neye
bağlandığını söyleyecek bir adı yoktur.

Bu ölçüt tek başına sınırı belirlemez, ama iki sonuç doğurur. Birincisi: bir bileşendeki tek
bir dosya değişse bileşenin tamamı yeniden yayınlanır ve bileşene bağlı olan herkes yeni
sürümü karşılamak zorunda kalır. İkincisi: bir bileşene bağlanan taraf, içindeki her dosyaya
bağlanmış olur.

Bu iki sonuç iki ayrı ilkeye dönüşür. **Ortak kapanış ilkesi** (common closure principle)
aynı nedenle aynı zamanda değişen dosyaların aynı bileşende toplanmasını ister; ölçüsü, bir
değişikliğin kaç bileşene dağıldığıdır. **Ortak yeniden kullanım ilkesi** (common reuse
principle) birlikte kullanılmayan dosyaların ayrı bileşenlerde durmasını ister; ölçüsü, bir
yeteneğe ihtiyaç duyan tarafın kaç dosyaya bağlandığıdır.

Bu üçü birlikte bileşenin **uyumunu** belirler. Bağlaşım ve Uyum konusunda uyum bir modülün
içindeki adların birbiriyle ilgililiği olarak ölçülmüştü; buradaki soru aynıdır, yalnız
kapsayan birim modül değil bileşendir ve ilgililiğin kanıtı adlar değil değişim geçmişidir.

## Kitaplığın Dosyaları ve Değişiklik Günlüğü

Kitaplık on iki dosyaya ulaşmış durumda. Aşağıdaki modül üç şeyi taşıyor: dosya listesi,
dosyalar arasındaki ithal kenarları ve yirmi iki değişikliğin her birinde birlikte dokunulan
dosyalar. Değişiklik günlüğü birinci derste karar düzeyinde tutulmuştu; burada dosya
düzeyindedir.

```js
// kitaplik.mjs — kitapligin dosyalari, ithal kenarlari ve yirmi iki degisiklik
export const DOSYALAR = [
  "ucret/tarife-tablosu.mjs", "ucret/ucret-kurali.mjs", "ucret/indirim.mjs",
  "bolge/posta-eslemesi.mjs", "bolge/katsayi.mjs",
  "tasiyici/secim.mjs", "tasiyici/tasiyici-listesi.mjs",
  "rota/aktarma-noktalari.mjs", "rota/rota-kurma.mjs",
  "teslimat/durum-akisi.mjs", "teslimat/zaman-damgasi.mjs", "ortak/para.mjs",
];

export const ITHAL = [
  ["ucret/ucret-kurali.mjs", "ucret/tarife-tablosu.mjs"],
  ["ucret/ucret-kurali.mjs", "bolge/katsayi.mjs"],
  ["ucret/ucret-kurali.mjs", "ortak/para.mjs"],
  ["ucret/indirim.mjs", "ortak/para.mjs"],
  ["ucret/tarife-tablosu.mjs", "ortak/para.mjs"],
  ["bolge/katsayi.mjs", "bolge/posta-eslemesi.mjs"],
  ["tasiyici/secim.mjs", "ucret/ucret-kurali.mjs"],
  ["tasiyici/secim.mjs", "ucret/indirim.mjs"],
  ["tasiyici/secim.mjs", "tasiyici/tasiyici-listesi.mjs"],
  ["rota/rota-kurma.mjs", "rota/aktarma-noktalari.mjs"],
  ["rota/rota-kurma.mjs", "tasiyici/tasiyici-listesi.mjs"],
  ["teslimat/durum-akisi.mjs", "teslimat/zaman-damgasi.mjs"],
  ["teslimat/durum-akisi.mjs", "rota/rota-kurma.mjs"],
];

export const GUNLUK = [
  ["ucret/tarife-tablosu.mjs", "bolge/katsayi.mjs"],
  ["ucret/tarife-tablosu.mjs", "bolge/posta-eslemesi.mjs", "bolge/katsayi.mjs"],
  ["ucret/ucret-kurali.mjs", "ucret/indirim.mjs"],
  ["ucret/tarife-tablosu.mjs", "bolge/katsayi.mjs"],
  ["rota/aktarma-noktalari.mjs", "rota/rota-kurma.mjs"],
  ["ucret/indirim.mjs", "tasiyici/secim.mjs"],
  ["ucret/tarife-tablosu.mjs", "bolge/posta-eslemesi.mjs"],
  ["teslimat/durum-akisi.mjs", "teslimat/zaman-damgasi.mjs"],
  ["ucret/ucret-kurali.mjs", "ucret/indirim.mjs", "tasiyici/secim.mjs"],
  ["ucret/tarife-tablosu.mjs", "bolge/katsayi.mjs", "bolge/posta-eslemesi.mjs"],
  ["rota/rota-kurma.mjs", "tasiyici/tasiyici-listesi.mjs"],
  ["ucret/tarife-tablosu.mjs", "bolge/katsayi.mjs"],
  ["teslimat/durum-akisi.mjs", "teslimat/zaman-damgasi.mjs"],
  ["ucret/indirim.mjs", "tasiyici/secim.mjs", "ucret/ucret-kurali.mjs"],
  ["rota/aktarma-noktalari.mjs", "rota/rota-kurma.mjs", "tasiyici/tasiyici-listesi.mjs"],
  ["ucret/tarife-tablosu.mjs", "bolge/posta-eslemesi.mjs", "bolge/katsayi.mjs"],
  ["teslimat/durum-akisi.mjs", "teslimat/zaman-damgasi.mjs"],
  ["ucret/indirim.mjs", "tasiyici/secim.mjs"],
  ["rota/aktarma-noktalari.mjs", "tasiyici/tasiyici-listesi.mjs", "rota/rota-kurma.mjs"],
  ["ucret/tarife-tablosu.mjs", "bolge/katsayi.mjs"],
  ["ucret/ucret-kurali.mjs", "ucret/indirim.mjs"],
  ["ortak/para.mjs", "ucret/ucret-kurali.mjs"],
];
```

Dizin adları bir bölünme öneriyor: `ucret`, `bolge`, `tasiyici`, `rota`, `teslimat`, `ortak`.
Bu bölünme dosyaların ne hakkında olduğuna bakılarak yapılmış. Günlük ise başka bir şey
söylüyor olabilir.

## Birlikte Değişenin Bulunması

**Birlikte değişim** (co-change), iki dosyanın aynı değişiklikte birlikte dokunulma
sayısıdır. Aşağıdaki araç bütün çiftler için bu sayıyı hesaplar, sonra bir eşiğin üstünde
kalan çiftleri aynı kümeye bağlar. Bağlama işlemi ayrık kümeler üzerinde yapılır: her dosya
kendi kökünde başlar, eşiği aşan her çift iki kökü birleştirir.

```js
// birlikte-degisim.mjs — birlikte degisen dosyalari esikle kumeler
import { DOSYALAR, GUNLUK } from "./kitaplik.mjs";

const ESIK = 3;
const anahtar = (a, b) => [a, b].sort().join(" + ");

const cift = new Map();
for (const dosyalar of GUNLUK) {
  for (let i = 0; i < dosyalar.length; i += 1) {
    for (let j = i + 1; j < dosyalar.length; j += 1) {
      const k = anahtar(dosyalar[i], dosyalar[j]);
      cift.set(k, (cift.get(k) ?? 0) + 1);
    }
  }
}

const kok = new Map(DOSYALAR.map((d) => [d, d]));
const bul = (d) => (kok.get(d) === d ? d : bul(kok.get(d)));
for (const [k, n] of cift) {
  if (n < ESIK) continue;
  const [a, b] = k.split(" + ");
  kok.set(bul(a), bul(b));
}

const kume = new Map();
for (const d of DOSYALAR) {
  const k = bul(d);
  kume.set(k, [...(kume.get(k) ?? []), d]);
}

console.log("birlikte degisim sayisi (esik >= " + ESIK + ")");
for (const [k, n] of [...cift].sort((a, b) => b[1] - a[1])) {
  console.log(`  ${n >= ESIK ? "*" : " "} ${String(n).padStart(2)}  ${k}`);
}
console.log(`\nkume sayisi = ${kume.size}`);
for (const [, uyeler] of kume) console.log(`  ${uyeler.join(", ")}`);
```

```
birlikte degisim sayisi (esik >= 3)
  *  7  bolge/katsayi.mjs + ucret/tarife-tablosu.mjs
  *  4  bolge/posta-eslemesi.mjs + ucret/tarife-tablosu.mjs
  *  4  ucret/indirim.mjs + ucret/ucret-kurali.mjs
  *  4  tasiyici/secim.mjs + ucret/indirim.mjs
  *  3  bolge/katsayi.mjs + bolge/posta-eslemesi.mjs
  *  3  rota/aktarma-noktalari.mjs + rota/rota-kurma.mjs
  *  3  teslimat/durum-akisi.mjs + teslimat/zaman-damgasi.mjs
  *  3  rota/rota-kurma.mjs + tasiyici/tasiyici-listesi.mjs
     2  tasiyici/secim.mjs + ucret/ucret-kurali.mjs
     2  rota/aktarma-noktalari.mjs + tasiyici/tasiyici-listesi.mjs
     1  ortak/para.mjs + ucret/ucret-kurali.mjs

kume sayisi = 5
  ucret/tarife-tablosu.mjs, bolge/posta-eslemesi.mjs, bolge/katsayi.mjs
  ucret/ucret-kurali.mjs, ucret/indirim.mjs, tasiyici/secim.mjs
  tasiyici/tasiyici-listesi.mjs, rota/aktarma-noktalari.mjs, rota/rota-kurma.mjs
  teslimat/durum-akisi.mjs, teslimat/zaman-damgasi.mjs
  ortak/para.mjs
```

Beş kümeden üçü dizin sınırlarını kesiyor. En yüksek sayı olan yedi, ücret tablosu ile bölge
katsayısı arasındadır: fiyat listesi yenilendiğinde ikisi birlikte yenileniyor, oysa ayrı
dizinlerde duruyorlar. İndirim ile taşıyıcı seçimi de dört kez birlikte değişmiş; indirim
oranı taşıyıcı sıralamasını etkilediği için ikisi tek bir ticari karardır.

Eşiğin üçte olması bir karardır, veri değil. İki dosyanın bir kez birlikte değişmesi tesadüf
olabilir; üç kez birlikte değişmesi bir nedene işaret eder. Eşik yükseltilirse küme sayısı
artar, düşürülürse kümeler birbirine yapışır.

## Üç Bölünmenin Ölçülmesi

Şimdi üç bölünme aynı ölçülerle karşılaştırılıyor: dizinlere göre bölünme, birlikte değişime
göre bölünme ve her şeyi tek bileşende toplayan bölünme. Ölçüler dörttür — bileşen sayısı,
bileşenler arası bağ sayısı, bir değişikliğin dağıldığı bileşen sayısı ve rota kurma
yeteneğine ihtiyaç duyan bir tüketicinin bağlanmak zorunda kaldığı dosya sayısı.

Son ölçü, bileşen çizgesinde rota bileşeninden çıkan kenarların ardışık izlenmesiyle bulunur:
bir bileşene bağlanan taraf, o bileşenin bağlandığı bileşenlere de bağlanmış olur.

```js
// bilesen-olc.mjs — uc bolunmeyi ayni uc olcuyle karsilastirir
import { DOSYALAR, ITHAL, GUNLUK } from "./kitaplik.mjs";

const dizinBolunmesi = Object.fromEntries(DOSYALAR.map((d) => [d, d.split("/")[0]]));
const KUMELER = [
  ["tarife", ["ucret/tarife-tablosu.mjs", "bolge/posta-eslemesi.mjs", "bolge/katsayi.mjs"]],
  ["ticari-kural", ["ucret/ucret-kurali.mjs", "ucret/indirim.mjs", "tasiyici/secim.mjs"]],
  ["ag", ["tasiyici/tasiyici-listesi.mjs", "rota/aktarma-noktalari.mjs", "rota/rota-kurma.mjs"]],
  ["izleme", ["teslimat/durum-akisi.mjs", "teslimat/zaman-damgasi.mjs"]],
  ["para", ["ortak/para.mjs"]],
];
const birlikteBolunmesi = Object.fromEntries(
  KUMELER.flatMap(([ad, uyeler]) => uyeler.map((d) => [d, ad])));
const tekBolunme = Object.fromEntries(DOSYALAR.map((d) => [d, "kitaplik"]));

function kapanis(bolunme, baslangic) {
  const kenar = new Set(ITHAL
    .filter(([a, b]) => bolunme[a] !== bolunme[b])
    .map(([a, b]) => `${bolunme[a]} -> ${bolunme[b]}`));
  const gorulen = new Set([bolunme[baslangic]]);
  for (const b of gorulen) {
    for (const k of kenar) {
      const [sol, sag] = k.split(" -> ");
      if (sol === b) gorulen.add(sag);
    }
  }
  return { kenarSayisi: kenar.size, dosyaSayisi: DOSYALAR.filter((d) => gorulen.has(bolunme[d])).length };
}

for (const [ad, bolunme] of [["dizin", dizinBolunmesi], ["birlikte", birlikteBolunmesi], ["tek", tekBolunme]]) {
  const bilesen = new Set(Object.values(bolunme));
  const dokunulan = GUNLUK.map((d) => new Set(d.map((f) => bolunme[f])).size);
  const yayin = dokunulan.reduce((a, b) => a + b, 0);
  const dagilan = dokunulan.filter((n) => n > 1).length;
  const { kenarSayisi, dosyaSayisi } = kapanis(bolunme, "rota/rota-kurma.mjs");
  console.log(`${ad.padEnd(9)} bilesen ${bilesen.size}  bilesenler arasi bag ${kenarSayisi}`);
  console.log(`          birden cok bilesene dokunan degisiklik ${dagilan} / ${GUNLUK.length}`);
  console.log(`          toplam yeniden yayin ${yayin}  ortalama ${(yayin / GUNLUK.length).toFixed(2)}`);
  console.log(`          rota kurmak icin baglanilan dosya ${dosyaSayisi} / ${DOSYALAR.length}`);
}
```

```
dizin     bilesen 6  bilesenler arasi bag 5
          birden cok bilesene dokunan degisiklik 16 / 22
          toplam yeniden yayin 38  ortalama 1.73
          rota kurmak icin baglanilan dosya 10 / 12
birlikte  bilesen 5  bilesenler arasi bag 5
          birden cok bilesene dokunan degisiklik 1 / 22
          toplam yeniden yayin 23  ortalama 1.05
          rota kurmak icin baglanilan dosya 3 / 12
tek       bilesen 1  bilesenler arasi bag 0
          birden cok bilesene dokunan degisiklik 0 / 22
          toplam yeniden yayin 22  ortalama 1.00
          rota kurmak icin baglanilan dosya 12 / 12
```

Dizin bölünmesinde yirmi iki değişikliğin on altısı birden çok bileşene dağılıyor; birlikte
değişim bölünmesinde bir tanesi. Toplam yeniden yayın sayısı 38'den 23'e iniyor. İki
bölünmenin bileşen sayıları ve bileşenler arası bağ sayıları neredeyse aynı — kazanç bileşen
saymaktan değil, sınırın nereden geçtiğinden geliyor.

Rota kurma ölçüsü daha keskin. Dizin bölünmesinde rota bileşeni taşıyıcı bileşenine
bağlanıyor, taşıyıcı bileşeni içindeki seçim dosyası ücrete, ücret bölgeye ve paraya; sonuçta
yalnız rota kurmak isteyen bir tüketici on iki dosyanın onuna bağlanmış oluyor. Birlikte
değişim bölünmesinde taşıyıcı listesi rota dosyalarıyla aynı bileşende olduğu için bu zincir
kuruluyor bile: üç dosya yetiyor.

## Üç İlkenin Gerilimi

Üçüncü satır ilkelerin birbirini ittiğini gösteriyor. Tek bileşenli bölünme ortak kapanış
ölçüsünde kusursuzdur — hiçbir değişiklik birden çok bileşene dağılmaz, çünkü tek bileşen
vardır. Aynı bölünme ortak yeniden kullanım ölçüsünde en kötü sonucu verir: bir tek yetenek
için on iki dosyanın hepsine bağlanılır.

Gerilim şu üç yönlüdür. Ortak kapanış bileşenleri **büyütmek** ister, çünkü birlikte değişen
her şey içeride olsun. Ortak yeniden kullanım **küçültmek** ister, çünkü kullanılmayan hiçbir
şey içeride olmasın. Yeniden kullanım–yayın eşdeğerliği ikisine de bir taban koyar: bölünme
ne olursa olsun, ayrı yayınlanamayacak kadar iç içe geçmiş bir küme bileşen sayılmaz.

Bu üçü aynı anda enbüyütülemez. Hangisinin ağır bastığı kitaplığın hangi aşamada olduğuna
bağlıdır: bileşen dışarıya henüz yayınlanmıyorsa geliştirmeyi zorlaştıran şey dağılan
değişikliklerdir ve ortak kapanış öne çıkar; bileşen çok sayıda tüketiciye yayınlanıyorsa
gereksiz bağımlılık pahalıdır ve ortak yeniden kullanım öne çıkar.

## Ölçünün Sınırı

Birlikte değişim bir işaret verir, karar vermez. Üç durumda yanıltır.

Birincisi, bütün dosyalara dokunan değişikliklerdir — toplu bir yeniden adlandırma ya da
biçim düzeltmesi bütün çiftlerin sayısını birden yükseltir ve kümeleri birbirine yapıştırır.
Böyle değişiklikler günlükten ayıklanmadan ölçüm anlamlı olmaz.

İkincisi, geçmişi az olan dosyalardır: bir dosyanın hiçbir çifti eşiği aşmıyorsa tek başına
bir kümede kalır. Yukarıdaki çıktıda `ortak/para.mjs` bu durumdadır — tek bir kez ücret
kuralıyla birlikte değişmiş ve kendi kümesinde kalmıştır. Sonuç bu örnekte doğrudur, çünkü
para birimi işlemleri gerçekten ayrı bir nedenle değişir; ama aynı biçim yeni eklenmiş her
dosya için de çıkar ve orada bir bilgi taşımaz.

Üçüncüsü, günlüğün geleceği değil geçmişi anlatmasıdır. Kitaplığın yeni bir alana açılması
kümeleri yeniden çizer. Bileşen sınırı bu yüzden bir kez çizilip bırakılan bir şey değil,
günlük büyüdükçe yeniden hesaplanan bir şeydir.

## Özet

- Bileşen, birlikte paketlenen ve birlikte yayınlanan dosya kümesidir; yeniden kullanım
  birimi yayın birimine eşittir.
- Ortak kapanış ilkesi birlikte değişenleri aynı bileşende toplar, ortak yeniden kullanım
  ilkesi birlikte kullanılmayanları ayırır; ikisi zıt yönde çeker.
- Yirmi iki değişikliğin birlikte değişim sayıları hesaplandığında beş küme çıktı ve üçü
  dizin sınırlarını kesti; en yüksek sayı ücret tablosu ile bölge katsayısı arasındaydı.
- Dizin bölünmesinde 22 değişikliğin 16'sı birden çok bileşene dağılıyordu, birlikte değişim
  bölünmesinde 1'i; toplam yeniden yayın 38'den 23'e indi.
- Rota kurma yeteneği için bağlanılan dosya sayısı 10'dan 3'e indi; her şeyi tek bileşende
  toplayan bölünmede aynı sayı 12 oldu ve iki ilkenin gerilimi görünür hâle geldi.

## Sonraki Adım

Bu dersin ölçüleri bileşenlerin **içine** bakıyordu: hangi dosyalar birlikte durmalı. Bileşen
çizgesinin kenarları ise sayıldı ama yönü sorgulanmadı. Bir kenarın hangi yöne bakması
gerektiğinin bir ölçütü var mıdır? Sık değişen bir bileşene bağlanan kararlı bir bileşen,
kendi kararlılığını kaybeder. Sonraki ders her bileşen için gelen ve giden bağ sayısından bir
kararlılık ölçüsü hesaplar, bağların kararlı yöne akıp akmadığını sayar ve aynı çizgede
bileşenler arası döngünün neden yayın sırasını olanaksız kıldığını gösterir.
