İçeriğe geç
academia.sh

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.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat