---
title: 'Değişeni Kapsülle'
source: 'https://academia.sh/tr/kurslar/tasarim-ilkeleri/degiseni-kapsulle'
course: 'Tasarım İlkeleri'
language: tr
updated: '2026-08-17T18:10:40+00:00'
license: 'CC BY-SA 4.0'
---

# Değişeni Kapsülle

Değişim eksenlerinin yalıtılması: fiyat listesi ve çıktı biçimi eksenlerinden gelen iki değişikliğin dokunduğu dosya kümelerinin çıkarılması, kesişimlerinin sayılması ve her ekseni kendi modülüne çeken yerleşimde kesişimin sıfıra inmesi.

Bir önceki dersin ayrık tasarımında ağırlık sınırı sabiti ile sevk kuralı aynı dosyadaydı.
İkisi aynı nedenle değişmiyor: sınır fiyat listesi yenilendiğinde, kural kurum politikası
değiştiğinde oynar. Bu farkın adı **değişim eksenidir**: bir modüle değişiklik isteği
gönderen ayrı kaynakların her biri bir eksendir.

**Değişeni kapsülle** (encapsulate what varies) ilkesi, her ekseni kendi modülünde toplamayı
söyler. Tek sorumluluk ilkesi aynı fikri değişim nedeni sayısıyla ölçmüştü; buradaki ölçü
farklı ve daha doğrudan: iki ayrı eksenden gelen iki değişikliğin dokunduğu dosya
kümelerinin **kesişimi**. Kesişim boş değilse, iki bağımsız değişiklik aynı metni paylaşıyor
demektir.

## İki Eksen, Dağıtık Yerleşim

Ölçüm, ücretlendirme kitaplığının en sık değişen iki ekseniyle yapılır: **fiyat listesi** ve
**çıktı biçimi**. Birincisini fiyatlandırma birimi, ikincisini muhasebe raporlaması ister;
ikisi aynı anda gelmez. Kitaplığın üç istemcisi var: HTTP ucu, toplu işleme ve rapor. Birinci
yerleşimde üçü de ücreti kendi içinde hesaplıyor ve satırı kendi içinde biçimliyor.

```sh
mkdir -p dagitik yalitilmis
```

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

```js
// dagitik/web.mjs — fiyat tablosu da cikti bicimi de burada
const KADEME = [[1, 3900], [5, 6400], [20, 11800]];
const BOLGE = { "34": 100, "06": 115, "65": 140 };

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

```js
// dagitik/toplu.mjs — ayni fiyat tablosu, ayni bicim, ikinci kopya
const KADEME = [[1, 3900], [5, 6400], [20, 11800]];
const BOLGE = { "34": 100, "06": 115, "65": 140 };

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

```js
// dagitik/rapor.mjs — ucuncu kopya
const KADEME = [[1, 3900], [5, 6400], [20, 11800]];
const BOLGE = { "34": 100, "06": 115, "65": 140 };

export function toplamSatiri(gonderiler) {
  let toplam = 0;
  for (const g of gonderiler) {
    const taban = KADEME.find(([ust]) => g.agirlik <= ust)?.[1] ?? 15000;
    toplam += Math.round((taban * (BOLGE[g.adres.slice(0, 2)] ?? 160)) / 100);
  }
  const kurus = toplam;
  return `TOPLAM ${kurus} kurus`;
}
```

## Yalıtılmış Yerleşim

İkinci yerleşimde her eksen kendi modülünde durur: fiyat listesi bir modülde, çıktı biçimi
bir modülde. İstemciler ikisini de dışarıdan alır.

```js
// yalitilmis/fiyat.mjs — birinci eksen: fiyat listesi yalniz burada
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
// yalitilmis/bicim.mjs — ikinci eksen: cikti bicimi yalniz burada
export const tutar = (kurus) => `${kurus} kurus`;
```

```js
// yalitilmis/web.mjs — iki ekseni de disaridan alir
import { kurusHesabi } from "./fiyat.mjs";
import { tutar } from "./bicim.mjs";

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

```js
// yalitilmis/toplu.mjs — iki ekseni de disaridan alir
import { kurusHesabi } from "./fiyat.mjs";
import { tutar } from "./bicim.mjs";

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

```js
// yalitilmis/rapor.mjs — iki ekseni de disaridan alir
import { kurusHesabi } from "./fiyat.mjs";
import { tutar } from "./bicim.mjs";

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

```js
// kur.mjs — iki yerlesimin ayni ciktiyi urettigini gosterir
import { KAYITLAR } from "./kayit.mjs";

const kok = process.argv[2];
const { satir } = await import(`./${kok}/web.mjs`);
const { satirlar } = await import(`./${kok}/toplu.mjs`);
const { toplamSatiri } = await import(`./${kok}/rapor.mjs`);

console.log(`${kok}: ${satir(KAYITLAR[0])} | ${satirlar(KAYITLAR).join(" ")} | ${toplamSatiri(KAYITLAR)}`);
```

```sh
node kur.mjs dagitik
node kur.mjs yalitilmis
```

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

## İki Değişiklik, İki Küme

Eksen A fiyat listesidir: orta kademe ücreti 6400'den 7100'e, Ankara bölge katsayısı 115'ten
120'ye çıkıyor. Eksen B çıktı biçimidir: tutarlar kuruş yerine iki ondalıklı lira olarak
yazılacak. İki değişiklik ayrı kopyalara ayrı ayrı uygulanır ve her birinin dokunduğu dosya
kümesi çıkarılır. Yerinde düzenlemede yedek uzantısı verilmiştir; GNU ve BSD `sed` bu biçimde
aynı davranır.

```sh
for k in dagitik yalitilmis; do
  cp -r $k $k-a
  cp -r $k $k-b
  sed -i.y 's/\[5, 6400\]/[5, 7100]/; s/"06": 115/"06": 120/' $k-a/*.mjs
  sed -i.y 's/\${kurus} kurus/${(kurus \/ 100).toFixed(2)} TL/' $k-b/*.mjs
  rm -f $k-a/*.y $k-b/*.y
  node kur.mjs $k-a
  node kur.mjs $k-b
done

degisen() { diff -rq "$1" "$2" | sed 's#^Files [^/]*/\([^ ]*\) and .*#\1#' | sort; }
for k in dagitik yalitilmis; do
  degisen $k $k-a > eksen-a.txt
  degisen $k $k-b > eksen-b.txt
  echo "$k eksen A -> $(tr '\n' ' ' < eksen-a.txt)"
  echo "$k eksen B -> $(tr '\n' ' ' < eksen-b.txt)"
  echo "$k kesisim = $(comm -12 eksen-a.txt eksen-b.txt | wc -l | tr -d ' ') $(comm -12 eksen-a.txt eksen-b.txt | tr '\n' ' ')"
  echo "$k bolge katsayisini bilen dosya = $(grep -l BOLGE $k/*.mjs | wc -l | tr -d ' ')"
done
```

```
dagitik-a: GN-1 3900 kurus | GN-1 3900 kurus GN-2 8520 kurus GN-3 16520 kurus | TOPLAM 28940 kurus
dagitik-b: GN-1 39.00 TL | GN-1 39.00 TL GN-2 73.60 TL GN-3 165.20 TL | TOPLAM 277.80 TL
yalitilmis-a: GN-1 3900 kurus | GN-1 3900 kurus GN-2 8520 kurus GN-3 16520 kurus | TOPLAM 28940 kurus
yalitilmis-b: GN-1 39.00 TL | GN-1 39.00 TL GN-2 73.60 TL GN-3 165.20 TL | TOPLAM 277.80 TL
dagitik eksen A -> rapor.mjs toplu.mjs web.mjs
dagitik eksen B -> rapor.mjs toplu.mjs web.mjs
dagitik kesisim = 3 rapor.mjs toplu.mjs web.mjs
dagitik bolge katsayisini bilen dosya = 3
yalitilmis eksen A -> fiyat.mjs
yalitilmis eksen B -> bicim.mjs
yalitilmis kesisim = 0
yalitilmis bolge katsayisini bilen dosya = 1
```

İki yerleşim de her iki değişiklikten sonra aynı sonucu üretiyor; fark maliyette. Dağıtık
yerleşimde her iki eksen de aynı üç dosyaya dokundu ve kesişim üç. Yalıtılmış yerleşimde
kümeler ayrık ve kesişim sıfır.

Son satır kesişimin nedenini gösteriyor: dağıtık yerleşimde bölge katsayısı üç dosyada
tanımlıydı, yalıtılmışta bir dosyada. Kesişimin kaynağı bilgiyi kopyalamaktır.

## Kesişimin Anlamı

Kesişim sayısı üç ayrı riskin ortak ölçüsüdür. Birincisi, iki bağımsız değişikliğin aynı anda
gelmesi durumunda aynı dosyaların düzenlenmesi gerekir; ikisi de aynı satırlara dokunuyorsa
birleştirme el işine döner. İkincisi, fiyat değişikliğini yapan kişi biçim kodunu da okumak
zorundadır — okuma yüzeyi, değişikliğin kendisiyle orantısız büyür. Üçüncüsü ve en pahalısı,
bir eksende yapılan değişikliğin diğer eksenin sınamalarını kırma olasılığıdır; kesişim sıfır
olduğunda bu olasılık yapısal olarak ortadan kalkar.

Ölçü tersinden de okunur. Kesişim sıfırsa ve bir eksende gelen değişiklik yine de iki dosyayı
düzenlettiyse, o eksen henüz tam yalıtılmamıştır. Yalıtımın hedefi dosya sayısını azaltmak
değil, kümeleri ayrık tutmaktır.

## Eksenin Doğru Seçilmesi

Yanlış eksen seçmek maliyeti azaltmaz, yerini değiştirir. Yukarıdaki bölme "hesap" ve
"biçim" olarak yapıldı; bunun yerine "web tarafı" ve "toplu taraf" biçiminde bir bölme
yapılsaydı iki eksenden gelen değişiklikler yine aynı dosyalara düşerdi, çünkü fiyat listesi
de biçim de her iki tarafta kullanılıyor.

Ekseni bulmanın yolu, tek bir kod tabanına bakmak değil, gelen değişiklik isteklerine
bakmaktır. İki istek hiç aynı anda gelmiyorsa ve farklı kişilerden geliyorsa iki ayrı
eksendir. Aynı kişiden, aynı gerekçeyle ve birlikte geliyorsa tek eksendir ve bölünmeleri
yalnız dosya sayısını artırır. Fiyat listesiyle çıktı biçimi bu ölçüte göre ayrıdır: birini
fiyatlandırma, diğerini muhasebe raporlaması ister.

## Özet

- Değişim ekseni, bir modüle değişiklik isteği gönderen ayrı kaynaklardır; ilke her ekseni
  kendi modülünde toplamayı söyler.
- Ölçü, iki ayrı eksenden gelen iki değişikliğin dokunduğu dosya kümelerinin kesişimidir.
- Dağıtık yerleşimde iki eksen de aynı üç dosyaya dokundu ve kesişim 3 çıktı; yalıtılmış
  yerleşimde kümeler ayrıktı ve kesişim 0 oldu.
- Kesişimin kaynağı bilgiyi kopyalamaktır: bölge katsayısı dağıtık yerleşimde 3 dosyada,
  yalıtılmışta 1 dosyada tanımlıydı.
- Eksen, kod yapısına değil gelen değişiklik isteklerinin kaynağına göre seçilir; yanlış eksen
  kesişimi azaltmaz.

## Sonraki Adım

Yalıtılmış yerleşimde üç istemci de `fiyat.mjs` modülünü adıyla ithal ediyor ve `kurusHesabi`
işlevini doğrudan çağırıyor. Fiyat hesabının ikinci bir gerçekleştirimi gerektiğinde — sözleşmeli
müşteriye özel bir liste, ya da sınama için sabit bir liste — istemcilerin ithal satırı
değişmek zorunda. Somut bir modüle bağlanmanın bedeli, gerçekleştirim değiştiğinde dokunulan
satır sayısıyla ölçülebilir. Sonraki ders bu bedeli sayar ve istemcinin somut tip yerine bir
soyutlamaya bağlandığı sürümle karşılaştırır.
