İçeriğe geç
academia.sh

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.
  • iade bayrağı 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.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat