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

# Uyum

Modül içindeki parçaların birbirine ait olup olmadığının sayılması: işlevlerin dokundukları modül düzeyi adlara göre kümelenmesi, bileşen sayısı ile paylaşmayan işlev çifti sayısının hesaplanması, düşük uyumlu modülün bölünüp ölçümün tekrarlanması ve bölmenin yeni bağlaşım kenarı üretmediğinin gösterilmesi.

Bağlaşım ölçüsü modüller arasındaki kenarları saydı ve modülün içine hiç bakmadı. Bir modül
dört ad dışa veriyor olabilir; bu dört adın birbirine ait olup olmadığı sorusu ölçünün
dışındadır. Aynı dosyada duran işlevler ortak bir veriye mi dokunuyor, yoksa yalnız aynı
dosyada oldukları için mi yan yanalar?

**Uyum** (cohesion) bir modül içindeki parçaların birbirine ait olma derecesidir ve
bağlaşımın modül içi karşılığıdır. Ölçülebilir tanımı şudur: bir modülün işlevleri,
dokundukları modül düzeyi adlara göre kümelendiğinde kaç ayrı kümeye ayrılıyor? Tek kümeye
ayrılıyorsa modül tek bir şeyle ilgilidir; birden çok kümeye ayrılıyorsa modül birkaç ayrı
şeyi aynı dosyada tutuyordur.

## Ölçülecek Modül

Aşağıdaki modül gönderiyle ilgili üç işi bir arada tutuyor: ücret hesabı, takip numarası
üretimi ve adres biçimleme. Üçü de "gönderi" sözcüğüyle anılabildiği için aynı dosyada
durmaları makul görünür.

```sh
mkdir -p birlesik bolunmus
```

```js
// birlesik/gonderi-servisi.mjs — gonderiyle ilgili her sey tek modulde
const KADEME = [[1, 3900], [5, 6400], [20, 11800]];
const BOLGE = { "34": 100, "06": 115, "65": 140 };
const IL_ADLARI = { "34": "Istanbul", "06": "Ankara", "65": "Van" };
const ONEK = "GN";
let SAYAC = 0;

export function taban(agirlik) {
  return KADEME.find(([ust]) => agirlik <= ust)?.[1] ?? 15000;
}

export function bolgeKatsayisi(adres) {
  return BOLGE[adres.slice(0, 2)] ?? 160;
}

export function ucret(gonderi) {
  return Math.round((taban(gonderi.agirlik) * bolgeKatsayisi(gonderi.adres)) / 100);
}

export function takipNo() {
  SAYAC += 1;
  return `${ONEK}-${String(SAYAC).padStart(6, "0")}`;
}

export function takipGecerli(no) {
  return new RegExp(`^${ONEK}-\\d{6}$`).test(no);
}

export function adresYaz(adres) {
  return `${IL_ADLARI[adres.slice(0, 2)] ?? "bilinmeyen"} ${adres}`;
}
```

## Kümeleme Ölçeri

Betik modülün üst düzey adlarını çıkarır, her işlevin gövdesinde hangi adların geçtiğini
bulur ve iki işlevi aynı kümeye koyar: ya ortak bir ada dokunuyorlarsa ya da biri diğerini
çağırıyorsa. Sonuçta bileşen sayısı ile ortak ada dokunan ve dokunmayan işlev çifti sayısı
raporlanır.

```js
// uyum-olc.mjs — modul icindeki islevleri dokunduklari adlara gore kumeler
import { readFileSync } from "node:fs";

function govde(metin, acilis) {
  let i = acilis;
  let derinlik = 0;
  do {
    if (metin[i] === "{") derinlik += 1;
    else if (metin[i] === "}") derinlik -= 1;
    i += 1;
  } while (i < metin.length && derinlik > 0);
  return metin.slice(acilis, i);
}

function coz(yol) {
  const metin = readFileSync(yol, "utf8");
  const veri = [...metin.matchAll(/^(?:export\s+)?(?:const|let)\s+(\w+)/gm)].map((m) => m[1]);
  const islevAdlari = [...metin.matchAll(/^(?:export\s+)?function\s+(\w+)/gm)].map((m) => m[1]);
  const tumu = [...veri, ...islevAdlari];
  const islevler = [];
  for (const m of metin.matchAll(/^(?:export\s+)?function\s+(\w+)\s*\(/gm)) {
    const g = govde(metin, metin.indexOf("{", m.index + m[0].length));
    const ref = tumu.filter((a) => a !== m[1] && new RegExp(`\\b${a}\\b`).test(g));
    islevler.push({ ad: m[1], ref: new Set(ref) });
  }
  return { veri, islevler };
}

const kesisiyor = (a, b) =>
  [...a.ref].some((x) => b.ref.has(x)) || a.ref.has(b.ad) || b.ref.has(a.ad);

for (const yol of process.argv.slice(2)) {
  const { veri, islevler } = coz(yol);
  const bilesen = new Map(islevler.map((f, i) => [f.ad, i]));
  let degisti = true;
  while (degisti) {
    degisti = false;
    for (const a of islevler) {
      for (const b of islevler) {
        if (a.ad !== b.ad && kesisiyor(a, b) && bilesen.get(a.ad) !== bilesen.get(b.ad)) {
          const enKucuk = Math.min(bilesen.get(a.ad), bilesen.get(b.ad));
          bilesen.set(a.ad, enKucuk);
          bilesen.set(b.ad, enKucuk);
          degisti = true;
        }
      }
    }
  }
  const kumeler = new Map();
  for (const [ad, k] of bilesen) kumeler.set(k, [...(kumeler.get(k) ?? []), ad]);

  let paylasan = 0;
  let paylasmayan = 0;
  for (let i = 0; i < islevler.length; i += 1) {
    for (let j = i + 1; j < islevler.length; j += 1) {
      if (kesisiyor(islevler[i], islevler[j])) paylasan += 1; else paylasmayan += 1;
    }
  }
  console.log(`${yol}  islev=${islevler.length} veri=${veri.length} bilesen=${kumeler.size}`);
  let n = 0;
  for (const [, adlar] of [...kumeler].sort((a, b) => a[0] - b[0])) {
    n += 1;
    console.log(`  bilesen ${n}: ${adlar.join(", ")}`);
  }
  console.log(`  paylasan cift=${paylasan} paylasmayan cift=${paylasmayan}` +
    ` uyumsuzluk=${Math.max(0, paylasmayan - paylasan)}`);
}
```

```sh
node uyum-olc.mjs birlesik/gonderi-servisi.mjs
```

```
birlesik/gonderi-servisi.mjs  islev=6 veri=5 bilesen=3
  bilesen 1: taban, bolgeKatsayisi, ucret
  bilesen 2: takipNo, takipGecerli
  bilesen 3: adresYaz
  paylasan cift=3 paylasmayan cift=12 uyumsuzluk=9
```

Altı işlev üç ayrı kümeye ayrıldı. On beş işlev çiftinden yalnız üçü ortak bir ada dokunuyor;
kalan on ikisinin hiçbir ortak noktası yok. Uyumsuzluk sayısı bu iki değerin farkıdır ve
dokuz çıktı. Modülün adı "gönderi servisi" olduğu için parçaların ilgili göründüğüne dikkat:
ölçüm ada değil, koda bakıyor.

## Bölme ve Yeniden Ölçüm

Bileşenler bölmenin sınırını doğrudan veriyor. Üç bileşen üç modüle çıkarılır; işlev
gövdeleri değişmez, yalnız hangi dosyada durdukları değişir.

```js
// bolunmus/ucret.mjs — birinci bilesen: ucret hesabi
const KADEME = [[1, 3900], [5, 6400], [20, 11800]];
const BOLGE = { "34": 100, "06": 115, "65": 140 };

export function taban(agirlik) {
  return KADEME.find(([ust]) => agirlik <= ust)?.[1] ?? 15000;
}

export function bolgeKatsayisi(adres) {
  return BOLGE[adres.slice(0, 2)] ?? 160;
}

export function ucret(gonderi) {
  return Math.round((taban(gonderi.agirlik) * bolgeKatsayisi(gonderi.adres)) / 100);
}
```

```js
// bolunmus/takip.mjs — ikinci bilesen: takip numarasi
const ONEK = "GN";
let SAYAC = 0;

export function takipNo() {
  SAYAC += 1;
  return `${ONEK}-${String(SAYAC).padStart(6, "0")}`;
}

export function takipGecerli(no) {
  return new RegExp(`^${ONEK}-\\d{6}$`).test(no);
}
```

```js
// bolunmus/adres.mjs — ucuncu bilesen: adres bicimleme
const IL_ADLARI = { "34": "Istanbul", "06": "Ankara", "65": "Van" };

export function adresYaz(adres) {
  return `${IL_ADLARI[adres.slice(0, 2)] ?? "bilinmeyen"} ${adres}`;
}
```

```sh
node uyum-olc.mjs bolunmus/ucret.mjs bolunmus/takip.mjs bolunmus/adres.mjs
grep -c "^import" bolunmus/ucret.mjs bolunmus/takip.mjs bolunmus/adres.mjs || true
```

```
bolunmus/ucret.mjs  islev=3 veri=2 bilesen=1
  bilesen 1: taban, bolgeKatsayisi, ucret
  paylasan cift=2 paylasmayan cift=1 uyumsuzluk=0
bolunmus/takip.mjs  islev=2 veri=2 bilesen=1
  bilesen 1: takipNo, takipGecerli
  paylasan cift=1 paylasmayan cift=0 uyumsuzluk=0
bolunmus/adres.mjs  islev=1 veri=1 bilesen=1
  bilesen 1: adresYaz
  paylasan cift=0 paylasmayan cift=0 uyumsuzluk=0
bolunmus/ucret.mjs:0
bolunmus/takip.mjs:0
bolunmus/adres.mjs:0
```

Üç modülün üçünde de bileşen sayısı bir, uyumsuzluk sıfır. Son üç satır bölmenin bedelini
sayıyor: yeni modüller birbirini hiç ithal etmiyor. Uyumsuz bir modülü bölmek bağlaşım
üretmedi, çünkü bölünen parçalar zaten birbirine dokunmuyordu. Bölmenin bağlaşım maliyeti
ancak gerçekten ortak veriye dokunan işlevler ayrıldığında doğar.

Davranış da değişmedi.

```js
// ayni-davranis.mjs — iki yerlesim ayni sonuclari uretir
const G = { agirlik: 3.0, adres: "06500" };
const b = await import("./birlesik/gonderi-servisi.mjs");
const u = await import("./bolunmus/ucret.mjs");
const t = await import("./bolunmus/takip.mjs");
const a = await import("./bolunmus/adres.mjs");

console.log("birlesik ->", b.ucret(G), b.takipNo(), b.adresYaz(G.adres), b.takipGecerli("GN-000001"));
console.log("bolunmus ->", u.ucret(G), t.takipNo(), a.adresYaz(G.adres), t.takipGecerli("GN-000001"));
```

```sh
node ayni-davranis.mjs
```

```
birlesik -> 7360 GN-000001 Ankara 06500 true
bolunmus -> 7360 GN-000001 Ankara 06500 true
```

## Uyumun Pratik Karşılığı

Bileşen sayısı soyut kalmasın diye ikinci bir ölçü alınır: bir verinin değişmesi hâlinde
okunması gereken dosyanın büyüklüğü. Takip numarası öneki ile bölge katsayıları farklı
kişilerin kararıdır; ikisinin de nerede durduğu ölçülebilir.

```js
// etki-alani.mjs — bir veri adi degistiginde okunmasi gereken dosyanin buyuklugu
import { readFileSync } from "node:fs";

const [ad, ...dosyalar] = process.argv.slice(2);

for (const yol of dosyalar) {
  const metin = readFileSync(yol, "utf8");
  if (!new RegExp(`^(?:const|let)\\s+${ad}\\b`, "m").test(metin)) continue;
  const islev = [...metin.matchAll(/^(?:export\s+)?function\s+\w+/gm)].length;
  const satir = metin.trim().split("\n").length;
  console.log(`${ad.padEnd(10)} ${yol.padEnd(30)} islev=${islev} satir=${satir}`);
}
```

```sh
for ad in ONEK BOLGE; do
  node etki-alani.mjs $ad birlesik/gonderi-servisi.mjs \
    bolunmus/ucret.mjs bolunmus/takip.mjs bolunmus/adres.mjs
done
```

```
ONEK       birlesik/gonderi-servisi.mjs   islev=6 satir=31
ONEK       bolunmus/takip.mjs             islev=2 satir=12
BOLGE      birlesik/gonderi-servisi.mjs   islev=6 satir=31
BOLGE      bolunmus/ucret.mjs             islev=3 satir=15
```

Takip önekini değiştirmek birleşik yerleşimde 6 işlevlik, 31 satırlık bir dosyayı açmayı
gerektiriyor; bölünmüş yerleşimde 2 işlevlik, 12 satırlık bir dosyayı. Uyumun karşılığı
budur: değişikliğin okuma yüzeyi, değişikliğin kendisiyle orantılı kalır.

## Yanıltıcı Uyum Ölçütleri

Adın benzerliği uyum değildir. `gonderi-servisi` adı altındaki altı işlev de gönderiyle
ilgiliydi; ölçüm yine üç bileşen buldu. Aynı biçimde "hepsi yardımcı işlev" ya da "hepsi
doğrulama yapıyor" gibi gerekçeler de ölçülemez; bu gruplamalarda işlevler ortak veri
paylaşmaz, yalnız benzer iş yaparlar.

Ters yönde bir yanılgı da vardır: bileşen sayısını bire indirmek her zaman doğru değildir.
Bir modülün bütün işlevlerini tek bir paylaşılan nesneye dokundurmak bileşen sayısını bire
düşürür ama bu, bir önceki derste ölçülen ortak bağlaşımı üretir. İki ölçü birlikte
okunmalıdır: modül içi bileşen sayısı bir, modüller arası yazma sayısı sıfır.

## Özet

- Uyum, bir modül içindeki işlevlerin dokundukları adlara göre kaç kümeye ayrıldığıdır;
  bağlaşımın modül içi karşılığıdır.
- Birleşik modülde 6 işlev 3 bileşene ayrıldı; 15 işlev çiftinden 12'si hiçbir ortak ada
  dokunmuyordu, uyumsuzluk 9 ölçüldü.
- Bileşenler bölmenin sınırını doğrudan verir: üç bileşen üç modüle çıkarıldığında her
  modülde bileşen sayısı 1, uyumsuzluk 0 oldu.
- Bölmenin bağlaşım maliyeti sıfır çıktı: yeni modüller birbirini hiç ithal etmiyor, çünkü
  ayrılan parçalar zaten ortak veriye dokunmuyordu.
- Bir verinin değişmesinde okunacak dosya 6 işlev ve 31 satırdan 2 işlev ve 12 satıra indi.

## Sonraki Adım

Uyum ölçüsü bir modülün kendi adlarına dokunmasına baktı. Modüller birbirine nesne
geçirmeye başladığında yeni bir soru çıkar: bir işlev, aldığı nesnenin içinden aldığı ikinci
bir nesnenin üçüncü bir alanına eriştiğinde, o işlev kaç ayrı tipin yapısını bilmek zorunda
kalır? `gonderi.alici.adres.il` gibi bir zincir, aradaki her adımın biçimine bağımlılık
üretir ve bu bağımlılık ne ithal grafiğinde ne de uyum ölçüsünde görünür. Sonraki ders zincir
derinliğini sayan bir tarama yazar ve zincir kısaldığında bir alan değişikliğinin dokunduğu
dosya sayısının nasıl düştüğünü ölçer.
