Ders 14 / 19
Politika ve Ayrıntı Ayrımı
Kararlı kural ile değişken ayrıntının ayrılması: gönderi ücretlendirme kitaplığının değişiklik günlüğünden karar başına değişim sayısının çıkarılması, tek dosyalı ve ayrılmış düzende dosya dokunuşunun sayılması ve ayrımın davranışı değiştirmediğinin gösterilmesi.
İçindekiler
Bağlaşım ve Uyum konusu denetimin ters çevrilmesiyle kapandı: üst düzey parçanın alt düzey parçayı çağırmak yerine ona çağrılma noktası vermesi. Buraya kadarki bütün ölçüler modül çiftleri üzerindeydi — iki modül arasındaki bağın gücü, bir modülün içindeki adların ilgililiği, bir çağrının kaç nesne üzerinden geçtiği.
Kod tabanı büyüdüğünde soru biçim değiştirir. Artık hangi modülün hangisine bağlanacağı değil, sınırın nereye çizileceği sorulur. Bu sorunun cevabı, kod parçalarının ne kadar sık değiştiğinde saklıdır: bir kısım karar yıllarca aynı kalır, bir kısmı ayda bir yenilenir. Birincisi politika (policy), ikincisi ayrıntı (detail) adını alır ve sınır ikisinin arasına çizilir.
Ayrımın Ölçütü
Politika ile ayrıntının farkı önem değil, değişim sıklığıdır. Gönderi ücretlendirme kitaplığında “ağırlık kademesinin ücreti bölge katsayısıyla çarpılır, sonuç yuvarlanır, asgari ücretin altına inemez” cümlesi kurumun kuralıdır ve kurum kural değiştirmedikçe durur. Kademe sınırlarının nerede olduğu, katsayıların kaç olduğu, yuvarlamanın kaç kuruşa yapıldığı ise fiyat listesi her yenilendiğinde değişir.
Aynı ayrımın bir de yön tarafı vardır: ayrıntı politikayı bilebilir, politika ayrıntıyı bilemez. Kural hangi kademede hangi ücretin olduğunu bilmeden de yazılabilir; tablo ise kuralın ondan ne beklediğini bilmek zorundadır.
Karışık Düzen
Kitaplığın ilk sürümünde iki taraf aynı dosyada duruyor.
mkdir -p tek-dosya
// tek-dosya/ucret.mjs — kural, tarife, bolge ve yuvarlama tek dosyada const KADEMELER = [ { ustAgirlik: 1, ucret: 4990 }, { ustAgirlik: 5, ucret: 8490 }, { ustAgirlik: 15, ucret: 14990 }, { ustAgirlik: 30, ucret: 24990 }, ]; const ASGARI_UCRET = 3990; const YUVARLAMA_ADIMI = 50; const KATSAYI = { yakin: 1, orta: 1.35, uzak: 1.8 }; const POSTA_BOLGE = { 34: "yakin", "06": "orta", 35: "orta", 65: "uzak" }; export function ucretHesapla(gonderi) { const kademe = KADEMELER.find((k) => gonderi.agirlik <= k.ustAgirlik); if (kademe === undefined) throw new RangeError("agirlik kademesi yok"); const bolge = POSTA_BOLGE[gonderi.postaKodu.slice(0, 2)] ?? "uzak"; const ham = kademe.ucret * KATSAYI[bolge]; return Math.max(Math.round(ham / YUVARLAMA_ADIMI) * YUVARLAMA_ADIMI, ASGARI_UCRET); }
Dosya kısa ve çalışıyor. Sorun onun okunmasında değil, dokunulma sıklığındadır. Bunu iddia etmek yerine saymak gerekir.
Değişikliğin Sayılması
Aşağıdaki modül kitaplığın on bir aylık geçmişini bir değişiklik günlüğü (change log) olarak taşıyor: her satır bir değişikliğin tarihi ve o değişikliğin dokunduğu kararlardır.
// degisiklik-gunlugu.mjs — kitapligin 24 degisikligi ve her birinin dokundugu kararlar export const KARARLAR = ["kural", "kademe-tablosu", "bolge-tablosu", "yuvarlama-adimi", "asgari-tutar"]; export const GUNLUK = [ ["2024-01-08", ["kademe-tablosu"]], ["2024-01-22", ["bolge-tablosu"]], ["2024-02-05", ["kademe-tablosu", "asgari-tutar"]], ["2024-02-19", ["kademe-tablosu"]], ["2024-03-04", ["bolge-tablosu"]], ["2024-03-18", ["kademe-tablosu"]], ["2024-04-01", ["kural"]], ["2024-04-15", ["kademe-tablosu"]], ["2024-04-29", ["bolge-tablosu"]], ["2024-05-13", ["kademe-tablosu", "asgari-tutar"]], ["2024-05-27", ["yuvarlama-adimi", "bolge-tablosu"]], ["2024-06-10", ["kademe-tablosu"]], ["2024-06-24", ["bolge-tablosu"]], ["2024-07-08", ["kademe-tablosu"]], ["2024-07-22", ["kural", "kademe-tablosu"]], ["2024-08-05", ["bolge-tablosu"]], ["2024-08-19", ["kademe-tablosu"]], ["2024-09-02", ["kademe-tablosu", "asgari-tutar"]], ["2024-09-16", ["kademe-tablosu"]], ["2024-09-30", ["bolge-tablosu"]], ["2024-10-14", ["kademe-tablosu", "yuvarlama-adimi"]], ["2024-10-28", ["bolge-tablosu"]], ["2024-11-11", ["kademe-tablosu", "asgari-tutar"]], ["2024-11-25", ["kural"]], ];
Günlük, kararları dosyalara eşleyen iki düzenle birleştirilir. Birincisi yukarıdaki tek dosyalı düzen, ikincisi ayrımın uygulandığı düzendir.
// dokunma-say.mjs — karar basina degisiklik sayisi ve iki duzende dosya dokunusu import { KARARLAR, GUNLUK } from "./degisiklik-gunlugu.mjs"; const TEK_DOSYA = Object.fromEntries(KARARLAR.map((k) => [k, "ucret.mjs"])); const AYRILMIS = { "kural": "politika/ucret-kurali.mjs", "kademe-tablosu": "ayrinti/tarife-tablosu.mjs", "yuvarlama-adimi": "ayrinti/tarife-tablosu.mjs", "asgari-tutar": "ayrinti/tarife-tablosu.mjs", "bolge-tablosu": "ayrinti/bolge-tablosu.mjs", }; function dosyaDokunusu(esleme) { const sayac = new Map(); for (const [, kararlar] of GUNLUK) { for (const dosya of new Set(kararlar.map((k) => esleme[k]))) { sayac.set(dosya, (sayac.get(dosya) ?? 0) + 1); } } return [...sayac].sort((a, b) => b[1] - a[1]); } for (const karar of KARARLAR) { const n = GUNLUK.filter(([, k]) => k.includes(karar)).length; console.log(`${karar.padEnd(16)} ${String(n).padStart(2)} degisiklik`); } console.log(`toplam degisiklik = ${GUNLUK.length}`); for (const [ad, esleme] of [["tek dosya", TEK_DOSYA], ["ayrilmis", AYRILMIS]]) { console.log(`\n${ad} duzeni`); for (const [dosya, n] of dosyaDokunusu(esleme)) { console.log(` ${dosya.padEnd(28)} ${String(n).padStart(2)} / ${GUNLUK.length}`); } }
node dokunma-say.mjs
kural 3 degisiklik kademe-tablosu 14 degisiklik bolge-tablosu 8 degisiklik yuvarlama-adimi 2 degisiklik asgari-tutar 4 degisiklik toplam degisiklik = 24 tek dosya duzeni ucret.mjs 24 / 24 ayrilmis duzeni ayrinti/tarife-tablosu.mjs 15 / 24 ayrinti/bolge-tablosu.mjs 8 / 24 politika/ucret-kurali.mjs 3 / 24
İlk beş satır ayrımın ölçütünü veriyor: kural yirmi dört değişikliğin üçünde,
kademe-tablosu on dördünde dokunulmuş. Aradaki dört katlık fark iki kararın aynı dosyada
durmasının gerekçesini ortadan kaldırıyor.
Alt bölümdeki iki blok sonucu gösteriyor. Tek dosyalı düzende kararlı kuralı içeren dosya 24 değişikliğin 24’ünde açılıyor. Ayrılmış düzende aynı kural 24 değişikliğin 3’ünde açılıyor; kalan 21 değişiklik ona hiç dokunmuyor.
Kararlı Kuralın Ayrılması
Ayrım, tabloları başka dosyaya taşımaktan ibaret değildir. Kuralın tabloları ithal etmemesi, ihtiyaç duyduğu işlemleri parametre olarak alması gerekir; aksi hâlde dosya ayrılır ama bağımlılık yönü aynı kalır.
mkdir -p ayrilmis/politika ayrilmis/ayrinti
// ayrilmis/politika/ucret-kurali.mjs — kararli kural: adimlarin kendisi ve sirasi export function ucretHesapla(gonderi, tarife, bolgeler) { const kademe = tarife.kademeBul(gonderi.agirlik); if (kademe === null) throw new RangeError("agirlik kademesi yok"); const katsayi = bolgeler.katsayi(gonderi.postaKodu); const yuvarlanmis = tarife.yuvarla(kademe.ucret * katsayi); return Math.max(yuvarlanmis, tarife.asgariUcret()); }
Dosyada tek bir sayı yok. Kural yalnız adımların kendisini ve sırasını söylüyor: kademeyi bul, katsayıyla çarp, yuvarla, asgarinin altına düşürme.
// ayrilmis/ayrinti/tarife-tablosu.mjs — degisken ayrinti: kademeler, adim, asgari tutar const KADEMELER = [ { ustAgirlik: 1, ucret: 4990 }, { ustAgirlik: 5, ucret: 8490 }, { ustAgirlik: 15, ucret: 14990 }, { ustAgirlik: 30, ucret: 24990 }, ]; const ASGARI_UCRET = 3990; const YUVARLAMA_ADIMI = 50; export const tarife = { kademeBul: (agirlik) => KADEMELER.find((k) => agirlik <= k.ustAgirlik) ?? null, yuvarla: (tutar) => Math.round(tutar / YUVARLAMA_ADIMI) * YUVARLAMA_ADIMI, asgariUcret: () => ASGARI_UCRET, };
// ayrilmis/ayrinti/bolge-tablosu.mjs — degisken ayrinti: posta kodu esdegeri ve katsayilar const KATSAYI = { yakin: 1, orta: 1.35, uzak: 1.8 }; const POSTA_BOLGE = { 34: "yakin", "06": "orta", 35: "orta", 65: "uzak" }; export const bolgeler = { bolgeBul: (postaKodu) => POSTA_BOLGE[postaKodu.slice(0, 2)] ?? "uzak", katsayi(postaKodu) { return KATSAYI[this.bolgeBul(postaKodu)]; }, };
İki ayrıntı dosyası da politikanın beklediği adları karşılıyor. Bu, Bağımlılığın Ters Çevrilmesi dersindeki yönün bu ölçekteki karşılığıdır: kararsız taraf kararlı tarafa bakıyor.
Ayrımın Bedeli Var mı
Bir yeniden düzenlemenin davranışı koruduğu iddia edilmez, sınanır. İki sürüm aynı gönderilerle çağrılır.
// esdeger-mi.mjs — ayrim davranisi degistirdi mi: iki surumu ayni gonderilerle karsilastirir import { ucretHesapla as tekDosya } from "./tek-dosya/ucret.mjs"; import { ucretHesapla as ayrilmis } from "./ayrilmis/politika/ucret-kurali.mjs"; import { tarife } from "./ayrilmis/ayrinti/tarife-tablosu.mjs"; import { bolgeler } from "./ayrilmis/ayrinti/bolge-tablosu.mjs"; const GONDERILER = [ { agirlik: 0.4, postaKodu: "34710" }, { agirlik: 3, postaKodu: "06800" }, { agirlik: 12, postaKodu: "65100" }, { agirlik: 28, postaKodu: "35400" }, { agirlik: 0.9, postaKodu: "81600" }, { agirlik: 5, postaKodu: "34100" }, ]; let ayrilan = 0; for (const g of GONDERILER) { const a = tekDosya(g), b = ayrilmis(g, tarife, bolgeler); if (a !== b) ayrilan += 1; console.log(`${String(g.agirlik).padStart(4)} kg ${g.postaKodu} ${String(a).padStart(6)} ${String(b).padStart(6)}`); } console.log(`ayrilan sonuc = ${ayrilan} / ${GONDERILER.length}`);
node esdeger-mi.mjs
0.4 kg 34710 5000 5000 3 kg 06800 11450 11450 12 kg 65100 27000 27000 28 kg 35400 33750 33750 0.9 kg 81600 9000 9000 5 kg 34100 8500 8500 ayrilan sonuc = 0 / 6
Altı gönderinin altısında sonuç aynı. Ayrımın getirdiği tek şey dosya sayısının birden üçe çıkması; buna karşılık kararlı kurala dokunan değişiklik oranı yüzde yüzden yüzde on ikiye inmiş durumda.
Politika Düzeyi
Ayrım ikili değildir. Bir kitaplıkta birden çok politika düzeyi bulunur ve düzeyin ölçüsü karara girdi-çıktıdan olan uzaklıktır. Ücret kuralı en yüksek düzeydedir: hiçbir dış kaynağa dokunmaz. Tarife tablosu bir düzey aşağıdadır: değerler dışarıdan gelir ama biçim kitaplığın kendi biçimidir. Posta kodunun ilk iki basamağının bölgeyi verdiği varsayımı en alt düzeydir; adres biçimi değişirse yalnız o kural değişir.
Kural tek cümleyle yazılır: bağımlılık okları daima daha yüksek politika düzeyine bakar. Bu ilkenin ihlali, bir sonraki dersin ölçtüğü şeyi bozar — kararlı tarafın karşı taraf hakkında bilmek zorunda olduğu ayrıntı sayısını artırır.
Özet
- Politika ile ayrıntının ayrımı önem sırasına göre değil, değişim sıklığına göre yapılır; ölçü değişiklik günlüğünden karar başına değişim sayısıdır.
- Kitaplığın yirmi dört değişikliğinde kural üç kez, kademe tablosu on dört kez değişti; dört katlık fark ikisinin aynı dosyada durmasını gerekçesiz bıraktı.
- Tek dosyalı düzende kararlı kurala 24 değişikliğin 24’ünde dokunuluyordu; ayrım sonrası bu sayı 3’e indi.
- Ayrım, tabloları taşımakla bitmez; kuralın tabloları ithal etmemesi, ihtiyaç duyduğu işlemleri parametre olarak alması gerekir.
- Altı gönderide iki sürümün sonucu aynı çıktı: yeniden düzenleme davranışı değiştirmedi, yalnız dokunuş dağılımını değiştirdi.
Sonraki Adım
Ayrılmış düzende kural, tarife nesnesinden üç ad bekliyor: kademe bulma, yuvarlama ve asgari ücret. Bu üç ad iki modül arasındaki sözleşmedir ve şu ana kadar sayılmadı. Sözleşme kaç addan oluşuyor, sınırdan kaç tip geçiyor, karşı tarafın bilmek zorunda olduğu alan sayısı kaç? Sonraki ders sözleşme yüzeyini ölçen bir araç yazar, geniş ve dar iki sınır tanımını aynı ölçüyle karşılaştırır ve sınır daraltıldığında karşı tarafa sızan ayrıntı sayısının nasıl düştüğünü gösterir.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.