Ders 08 / 16
Mantıksal Bayrak Parametreleri
Doğruluk değeri taşıyan parametrelerin bedelinin sınamayla ölçülmesi: iki bayraklı tek fonksiyonun ve bölünmüş iki fonksiyonun dallarını kapsayan sınama sayılarının node --test ile karşılaştırılması, bayrağın gövdede açtığı dallanma sayısı ve çağrı yerindeki opak argümanlar.
İçindekiler
Önceki derste imzadan boş değerler ayıklandı. Benzer bir sorun başka bir kılıkta geri geliyor. Ücretlendirme kitaplığı iade gönderilerini de hesaplıyor ve iki hesap büyük ölçüde ortak: aynı kademe seçimi, aynı ağırlık, farklı çarpan ve farklı asgari ücret. Farkı taşımak için imzaya bir doğruluk değeri eklemek akla yakın görünür.
Bir mantıksal bayrak (boolean flag) parametresi, fonksiyonun iki farklı iş yaptığını imzada ilan eder. Bedeli iki yerde ortaya çıkar: gövdede açtığı dallanmalarda ve çağrı yerinde hiçbir şey söylemeyen argümanda. İkisi de sayılabilir.
İki Bayraklı Sürüm
Fonksiyon iki bayrak alıyor: gönderinin iade olup olmadığı ve sigortalı olup olmadığı.
mkdir -p bayrakli bolunmus
// bayrakli/ucret.mjs — iade ve sigorta kararlari iki bayrakla tasiniyor export function ucretHesapla(gonderi, tarife, iade, sigortali) { const kademe = tarife.agirlikKademeleri.find((k) => gonderi.agirlikKg <= k.ustSinirKg); const temel = kademe.kiloBasiUcret * gonderi.agirlikKg; const ucret = iade ? temel * 0.6 : temel; const sigorta = sigortali && iade === false ? gonderi.beyanDegeri * 0.01 : 0; const asgari = iade ? tarife.asgariIadeUcreti : tarife.asgariUcret; return Math.max(ucret + sigorta, asgari); }
Bayrak imzada bir kez geçer, gövdede üç yerde daha karşımıza çıkar: ücret çarpanında, sigortanın bastırılmasında ve asgari ücret seçiminde.
tail -n +2 bayrakli/ucret.mjs | grep -c iade
4
Sayı, bayrağın tek bir anahtar olmadığını gösteriyor. iade bir açma kapama düğmesi
değil, fonksiyonun gövdesini baştan sona ikiye bölen bir ayrımdır. İkinci bayrak birinciye
bağımlı: sigorta yalnız iade olmayan gönderilerde uygulanıyor, yani iade === false
koşulu sigortanın da içine giriyor.
Bölünmüş Sürüm
İkinci sürümde iki bayrak da kayboluyor. iade bayrağı iki ayrı fonksiyona bölünüyor;
sigortali bayrağı ise zaten veride bulunan bir bilgiyi tekrar ediyordu — beyan değeri
sıfır olan bir gönderinin sigorta ücreti de sıfırdır.
// bolunmus/ucret.mjs — iki ayri islem, iki ayri fonksiyon function temelUcret(gonderi, tarife) { const kademe = tarife.agirlikKademeleri.find((k) => gonderi.agirlikKg <= k.ustSinirKg); return kademe.kiloBasiUcret * gonderi.agirlikKg; } export function gonderiUcreti(gonderi, tarife) { const sigorta = gonderi.beyanDegeri * 0.01; return Math.max(temelUcret(gonderi, tarife) + sigorta, tarife.asgariUcret); } export function iadeUcreti(gonderi, tarife) { return Math.max(temelUcret(gonderi, tarife) * 0.6, tarife.asgariIadeUcreti); }
“İade gönderisine sigorta uygulanmaz” kuralı artık bir koşul değil, iadeUcreti
fonksiyonunun kendisidir. Kuralı bozmak için fonksiyonu değiştirmek gerekir; bayraklı
sürümde bir çağrıda yanlış birleşim geçirmek yetiyordu.
Sınamayla Ölçüm
İki sürüm de aynı tarifeyle ve aynı iki gönderiyle sınanıyor. Ölçüt sabit: her dalın en az bir sınamayla geçilmesi. Bayraklı sürümde iki bayrağın dört birleşimi var ve her birleşim asgari ücretin devrede olduğu ve olmadığı durumda ayrı ayrı sınanmalı.
// tarife.mjs — iki sinamanin paylastigi tarife ve gonderiler export const TARIFE = { agirlikKademeleri: [{ ustSinirKg: 1, kiloBasiUcret: 38 }, { ustSinirKg: 5, kiloBasiUcret: 26 }, { ustSinirKg: 10, kiloBasiUcret: 21 }, { ustSinirKg: 30, kiloBasiUcret: 17 }], asgariUcret: 52, asgariIadeUcreti: 40, }; export const BUYUK = { agirlikKg: 4, beyanDegeri: 2000 }; export const KUCUK = { agirlikKg: 0.5, beyanDegeri: 0 };
// test-bayrakli.mjs — iki bayragin dort birlesimi, her birinde asgari ucret siniri import { test } from "node:test"; import assert from "node:assert/strict"; import { ucretHesapla } from "./bayrakli/ucret.mjs"; import { TARIFE, BUYUK, KUCUK } from "./tarife.mjs"; test("gonderi, sigortasiz, asgari uzeri", () => { assert.equal(ucretHesapla(BUYUK, TARIFE, false, false), 104); }); test("gonderi, sigortasiz, asgari altinda", () => { assert.equal(ucretHesapla(KUCUK, TARIFE, false, false), 52); }); test("gonderi, sigortali, asgari uzeri", () => { assert.equal(ucretHesapla(BUYUK, TARIFE, false, true), 124); }); test("gonderi, sigortali, asgari altinda", () => { assert.equal(ucretHesapla(KUCUK, TARIFE, false, true), 52); }); test("iade, sigortasiz, asgari uzeri", () => { assert.equal(ucretHesapla(BUYUK, TARIFE, true, false), 62.4); }); test("iade, sigortasiz, asgari altinda", () => { assert.equal(ucretHesapla(KUCUK, TARIFE, true, false), 40); }); test("iade, sigortali, sigorta bastirilir", () => { assert.equal(ucretHesapla(BUYUK, TARIFE, true, true), 62.4); }); test("iade, sigortali, asgari altinda", () => { assert.equal(ucretHesapla(KUCUK, TARIFE, true, true), 40); });
Bölünmüş sürümde birleşim yok; her fonksiyonun tek bir sınırı var.
// test-bolunmus.mjs — iki fonksiyon, her birinde asgari ucret siniri import { test } from "node:test"; import assert from "node:assert/strict"; import { gonderiUcreti, iadeUcreti } from "./bolunmus/ucret.mjs"; import { TARIFE, BUYUK, KUCUK } from "./tarife.mjs"; test("gonderi ucreti, asgari uzeri", () => { assert.equal(gonderiUcreti(BUYUK, TARIFE), 124); }); test("gonderi ucreti, asgari altinda", () => { assert.equal(gonderiUcreti(KUCUK, TARIFE), 52); }); test("iade ucreti, asgari uzeri", () => { assert.equal(iadeUcreti(BUYUK, TARIFE), 62.4); }); test("iade ucreti, asgari altinda", () => { assert.equal(iadeUcreti(KUCUK, TARIFE), 40); });
Sayım, çalıştırıcının kendi özetinden okunur. Süre alanları çalıştırmadan çalıştırmaya değiştiği için yalnız sayaç satırları alınıyor.
for t in test-bayrakli.mjs test-bolunmus.mjs; do echo "== $t" node --test "$t" 2>&1 | grep -E '(tests|pass|fail) [0-9]+$' done
== test-bayrakli.mjs ℹ tests 8 ℹ pass 8 ℹ fail 0 == test-bolunmus.mjs ℹ tests 4 ℹ pass 4 ℹ fail 0
Sekiz sınamaya karşı dört sınama. İki sürüm aynı dört sonucu (104, 124, 62,4 ve 40 ile asgari ücret sınırları) üretiyor; fark, bayrakların birleşimlerinin ayrıca sınanmak zorunda olmasından geliyor.
İkinci ölçüm çağrı yerinde. İlk derste tanımlanan opak argüman ölçütü doğruluk değerleri
için doğrudan geçerlidir: çıplak bir true çağrı satırında hangi parametreyi doldurduğunu
söylemez.
for t in test-bayrakli.mjs test-bolunmus.mjs; do echo "$t: $(grep -o 'true\|false' $t | wc -l | tr -d ' ') opak dogruluk argumani" done
test-bayrakli.mjs: 16 opak dogruluk argumani test-bolunmus.mjs: 0 opak dogruluk argumani
ucretHesapla(BUYUK, TARIFE, true, false) satırını okuyan kişi ikinci false değerinin
sigortayı mı yoksa başka bir seçeneği mi kapattığını bilemez; imzayı açmak zorundadır.
iadeUcreti(BUYUK, TARIFE) satırında sorulacak bir soru kalmaz.
Bayrak Sayısı Arttığında
İki bayrak dört birleşim üretti. Üçüncü bir bayrak eklendiğinde birleşim sayısı sekize çıkar; sınama sayısı da aynı oranda artar. Bölünmüş tasarımda üçüncü bir iş üçüncü bir fonksiyondur ve toplam iki değil bir artar. Bayraklı imza çarpar, bölünmüş imza toplar.
Bu, önceki derste ölçülen dallanma davranışının aynısıdır: aynı gövdede duran bağımsız kararlar çarpılır. Bayrak parametresi bu çarpımı fonksiyonun dışına taşır; artık birleşimleri üreten şey gövdedeki koşullar değil, çağıran tarafın geçirdiği değerlerdir.
Bayrağın Meşru Olduğu Yer
Her doğruluk değeri parametresi kusur değildir. Ayırt edici soru şudur: bayrak fonksiyonun ne yaptığını mı yoksa nasıl yaptığını mı değiştiriyor?
iade bayrağı ne yapıldığını değiştiriyordu — iki ayrı iş vardı ve ikisi tek imzaya
sıkıştırılmıştı. Buna karşılık bir biçimlendirme fonksiyonuna geçirilen “kuruş kısmını
göster” bayrağı aynı işin bir ayrıntısını belirler; iş tektir. Böyle bir parametre yine de
çağrı yerinde opaktır ve adlandırılmış bir alan olarak geçirildiğinde
({ kurusGoster: true }) opaklık ortadan kalkar.
İkinci ölçüt, bayrağın veriyle çakışıp çakışmadığıdır. sigortali bayrağı bunun örneğiydi:
beyan değeri zaten sigortanın uygulanıp uygulanmayacağını söylüyordu. Aynı bilgiyi iki
yerde tutmak, ikisinin ayrı düşmesine izin verir — sigortalı işaretlenmiş ama beyan değeri
sıfır olan bir gönderi tutarsızdır ve bayraklı imza bu tutarsızlığı kabul eder.
Özet
- Mantıksal bayrak parametresi fonksiyonun iki iş yaptığını ilan eder; bedeli gövdedeki dallanmalarda ve çağrı yerindeki opak argümanda ortaya çıkar.
iadebayrağı imzada bir kez geçiyor, dosyada dört satırda görünüyordu: ücret çarpanı, sigortanın bastırılması ve asgari ücret seçimi ayrı ayrı ona bağlıydı.- İki bayrağın dallarını kapsamak sekiz sınama gerektirdi; aynı davranışı iki ayrı fonksiyon olarak sınamak dört sınamayla tamamlandı.
- Çağrı yerlerinde on altı opak doğruluk argümanı sıfıra indi; bölünmüş imzalar çağrı satırında soru bırakmıyor.
- Bayrak sayısı arttıkça birleşimler çarpılır, ayrı fonksiyonlar toplanır; bayrak yalnız işin ne olduğunu değil nasıl yapıldığını belirlediğinde ve veriyle çakışmadığında savunulabilir.
Sonraki Adım
İmza kararları bir fonksiyonun dışarıya ne söylediğini belirliyor. Aynı soru modül
düzeyinde de sorulabilir: bir modül dışarıya kaç ad açıyor? bolunmus/ucret.mjs
dosyasında temelUcret dışa aktarılmadı, iki ücret fonksiyonu aktarıldı. Bu karar
keyfî değil; dışa açılan her ad, o modülün değiştirilemez yüzeyine eklenen bir maddedir.
Sonraki ders bu yüzeyi ölçer: bir modülün dışa açtığı adların kaçı gerçekten kullanılıyor
ve açılan her ad modülün değişme özgürlüğünü ne kadar daraltıyor.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.