İçeriğe geç
academia.sh

Ders 17 / 19

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ı.

İçindekiler

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.

// 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.

// 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.

// 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.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat