Ders 14 / 30
Strateji
Ücret hesaplama algoritmasının çağrı yerine gömüldüğü tasarım ile tarifenin parametre olarak geçildiği tasarımın karşılaştırılması: tek koşumda denenebilen algoritma sayısı, yeni tarife eklendiğinde düzenlenen dosya ve satır sayısı, kalıbın bedeli olarak eklenen dosya sayısı ve çağrı yerinden izlenmesi gereken olası gövde sayısı.
İçindekiler
Yapısal kalıplar nesnelerin nasıl birleştirileceğini düzenledi: bağdaştırıcı uyumsuz iki arayüzü köprüledi, dekoratör davranışı katmanladı, bileşik ağacı tek biçim ele aldı, vekil ise araya girip çağrıyı aynı arayüzle ilerisine geçirdi. Vekilde araya giren nesnenin işi çağrıyı geçirmekti; hesabın kendisi değişmiyordu. Sıradaki soru birleşme değil: aynı arayüzün arkasındaki hesabın kendisi çalışma zamanında değiştirilebilir mi?
Ücretlendirme kitaplığında bu somut bir istektir. Aynı gönderi kümesi standart tarifeyle, ekspres tarifeyle ve hacimden türetilen ücretli ağırlık tarifesiyle fiyatlanmak istenir; üçü de aynı koşumda, aynı veriyle. Strateji (strategy) kalıbı bunu, algoritmayı bir nesneye alıp çağrı yerine parametre olarak geçirerek yapar. Koşul zinciri, arama tablosu ve çok biçimlilik arasındaki seçim Temiz Kod ve Programlama Paradigmaları kurslarında ölçülmüştü; bu derste ölçülen şey başka: hangi dosyaların düzenlenmesi gerektiği ve tek koşumda kaç algoritmanın yan yana durabildiği.
İki Tasarım
Ortak veri her iki sürümde aynıdır. Tutarlar kuruş cinsindendir, ağırlık kilogram, hacim desimetreküp.
mkdir -p gomulu strateji
// veri.mjs — ucretlendirilecek gonderiler (agirlik kg, hacim dm3, tutarlar kurus) export const GONDERILER = [ { kod: "GN-1", agirlik: 2, hacim: 8, bolge: "1" }, { kod: "GN-2", agirlik: 6, hacim: 60, bolge: "2" }, { kod: "GN-3", agirlik: 14, hacim: 20, bolge: "3" }, { kod: "GN-4", agirlik: 1, hacim: 3, bolge: "2" }, { kod: "GN-5", agirlik: 22, hacim: 180, bolge: "3" }, ];
Birinci tasarımda formül, onu çağıran modülün ithal ettiği tek gövdenin içindedir. Toplu fiyatlama hangi algoritmayı çalıştırdığını seçmez; ithal satırı seçmiştir.
// gomulu/ucret.mjs — tarife formulu govdenin icinde sabit const BOLGE_KATSAYI = { "1": 100, "2": 130, "3": 175 }; export function ucret(gonderi) { const ham = 2500 + 420 * Math.ceil(gonderi.agirlik); return Math.max(Math.round((ham * BOLGE_KATSAYI[gonderi.bolge]) / 100), 3900); }
// gomulu/toplu.mjs — toplu fiyatlama; algoritma ithalle sabitlenmis import { ucret } from "./ucret.mjs"; export const topluUcret = (gonderiler) => gonderiler.map((g) => ({ kod: g.kod, tutar: ucret(g) }));
İkinci tasarımda her tarife kendi dosyasında bir nesnedir. Nesnelerin tek ortak sözleşmesi
ucret(gonderi) yöntemidir; paylaştıkları tek veri bölge katsayılarıdır.
// strateji/bolge.mjs — bolge katsayilari, tarifelerin paylastigi tek veri export const BOLGE_KATSAYI = { "1": 100, "2": 130, "3": 175 }; export const bolgele = (ham, bolge) => Math.round((ham * BOLGE_KATSAYI[bolge]) / 100);
// strateji/tarife-standart.mjs import { bolgele } from "./bolge.mjs"; export const standart = { ad: "standart", ucret(gonderi) { return Math.max(bolgele(2500 + 420 * Math.ceil(gonderi.agirlik), gonderi.bolge), 3900); }, };
// strateji/tarife-ekspres.mjs import { bolgele } from "./bolge.mjs"; export const ekspres = { ad: "ekspres", ucret(gonderi) { return Math.max(bolgele(4000 + 640 * Math.ceil(gonderi.agirlik), gonderi.bolge), 6500); }, };
// strateji/tarife-hacimsel.mjs — ucretli agirlik hacimden turetilir import { bolgele } from "./bolge.mjs"; export const hacimsel = { ad: "hacimsel", ucret(gonderi) { const ucretliAgirlik = Math.max(Math.ceil(gonderi.agirlik), Math.ceil(gonderi.hacim / 5)); return Math.max(bolgele(2500 + 420 * ucretliAgirlik, gonderi.bolge), 3900); }, };
// strateji/toplu.mjs — tarife parametre olarak gelir export const topluUcret = (gonderiler, tarife) => gonderiler.map((g) => ({ kod: g.kod, tutar: tarife.ucret(g) }));
Toplu fiyatlama artık hiçbir tarifeyi ithal etmiyor. Bildiği tek şey, aldığı nesnenin bir
ucret yöntemi taşıdığıdır.
Tek Koşumda Kaç Algoritma
İlk ölçü, aynı süreçte aynı veriye kaç ayrı algoritmanın uygulanabildiğidir. Sürücü betik iki sürümü de çağırır.
// kosum.mjs — tek kosumda kac tarife denenebiliyor import { GONDERILER } from "./veri.mjs"; import { topluUcret as gomuluToplu } from "./gomulu/toplu.mjs"; import { topluUcret as stratejiToplu } from "./strateji/toplu.mjs"; import { standart } from "./strateji/tarife-standart.mjs"; import { ekspres } from "./strateji/tarife-ekspres.mjs"; import { hacimsel } from "./strateji/tarife-hacimsel.mjs"; const yaz = (etiket, satirlar) => console.log(`${etiket.padEnd(18)} ${satirlar.map((s) => `${s.kod}=${s.tutar}`).join(" ")}`); const gomuluSonuc = [gomuluToplu(GONDERILER)]; yaz("gomulu", gomuluSonuc[0]); const stratejiSonuc = [standart, ekspres, hacimsel].map((t) => { const satirlar = stratejiToplu(GONDERILER, t); yaz(`strateji/${t.ad}`, satirlar); return satirlar; }); console.log(`gomulu : tek kosumda denenen tarife = ${gomuluSonuc.length}`); console.log(`strateji : tek kosumda denenen tarife = ${stratejiSonuc.length}`);
gomulu GN-1=3900 GN-2=6526 GN-3=14665 GN-4=3900 GN-5=20545 strateji/standart GN-1=3900 GN-2=6526 GN-3=14665 GN-4=3900 GN-5=20545 strateji/ekspres GN-1=6500 GN-2=10192 GN-3=22680 GN-4=6500 GN-5=31640 strateji/hacimsel GN-1=3900 GN-2=9802 GN-3=14665 GN-4=3900 GN-5=30835 gomulu : tek kosumda denenen tarife = 1 strateji : tek kosumda denenen tarife = 3
Bir ve üç. Gömülü sürümde ikinci bir tarifeyi denemenin yolu kaynağı düzenleyip süreci yeniden başlatmaktır; üç tarifeyi karşılaştıran bir rapor üç ayrı koşum ve arada iki düzenleme demektir. Strateji sürümünde üç sonuç aynı süreçte yan yana durur, bu yüzden karşılaştırma raporu bir işlev çağrısıdır. Çıktının ilk iki satırının aynı olması ölçümün geçerlilik koşuludur: strateji sürümü standart tarifede gömülü sürümle birebir aynı tutarı üretiyor, yani karşılaştırılan iki tasarım aynı davranışı taşıyor.
Dördüncü Tarife Eklendiğinde
İkinci ölçü değişim maliyetidir. Ekonomik adında dördüncü bir tarife isteniyor: tabanı 1800, kademesi 300, asgari ücreti 3200. İki ağaç kopyalanır ve istek ikisine de uygulanır. Gömülü sürümde gövdenin kendisi ve onu çağıran modülün imzası; strateji sürümünde tek bir yeni dosya.
cp -r gomulu gomulu-yeni cp -r strateji strateji-yeni cat > gomulu-yeni/ucret.mjs <<'SON' // gomulu/ucret.mjs — tarife formulu govdenin icinde sabit const BOLGE_KATSAYI = { "1": 100, "2": 130, "3": 175 }; export function ucret(gonderi, mod = "standart") { const taban = mod === "ekonomik" ? 1800 : 2500; const kademe = mod === "ekonomik" ? 300 : 420; const asgari = mod === "ekonomik" ? 3200 : 3900; const ham = taban + kademe * Math.ceil(gonderi.agirlik); return Math.max(Math.round((ham * BOLGE_KATSAYI[gonderi.bolge]) / 100), asgari); } SON cat > gomulu-yeni/toplu.mjs <<'SON' // gomulu/toplu.mjs — toplu fiyatlama; algoritma ithalle sabitlenmis import { ucret } from "./ucret.mjs"; export const topluUcret = (gonderiler, mod) => gonderiler.map((g) => ({ kod: g.kod, tutar: ucret(g, mod) })); SON cat > strateji-yeni/tarife-ekonomik.mjs <<'SON' // strateji/tarife-ekonomik.mjs — var olan dosyalara dokunulmadi import { bolgele } from "./bolge.mjs"; export const ekonomik = { ad: "ekonomik", ucret(gonderi) { return Math.max(bolgele(1800 + 300 * Math.ceil(gonderi.agirlik), gonderi.bolge), 3200); }, }; SON for k in gomulu strateji; do duzenlenen=$(diff -rq "$k" "$k-yeni" | grep -c '^Files') eklenen=$(diff -rq "$k" "$k-yeni" | grep -c '^Only in') satir=$(diff -r -u "$k" "$k-yeni" | grep '^[+-][^+-]' | grep -vc '^[+-]//') echo "$k: duzenlenen dosya=$duzenlenen yeni dosya=$eklenen duzenlenen satir=$satir" done echo "dosya sayisi: gomulu=$(ls gomulu | wc -l | tr -d ' ') strateji=$(ls strateji | wc -l | tr -d ' ')" echo "cagri yerinden olasi govde: gomulu=1 strateji=$(grep -l 'ucret(gonderi)' strateji/tarife-*.mjs | wc -l | tr -d ' ')"
gomulu: duzenlenen dosya=2 yeni dosya=0 duzenlenen satir=13 strateji: duzenlenen dosya=0 yeni dosya=1 duzenlenen satir=0 dosya sayisi: gomulu=2 strateji=5 cagri yerinden olasi govde: gomulu=1 strateji=3
Gömülü sürümde iki dosya ve on üç satır düzenlendi; bu satırların bir kısmı çalışan tarifelerin de geçtiği gövdedir, yani dördüncü tarifeyi eklerken ilk üçünün davranışı riske girer. Strateji sürümünde düzenlenen dosya sıfırdır. Açık–kapalı ilkesinin ölçüsü tam olarak budur ve strateji kalıbı bu ilkeyi tarife ekseninde sağlar.
Kalıbın Bedeli
Aynı çıktının son iki satırı bedeli veriyor. Dosya sayısı 2’den 5’e çıktı: üç tarife dosyası
ve paylaşılan bölge modülü. Bedelin ikinci kalemi okuma maliyetidir. Gömülü sürümde ucret
çağrısından tek bir gövdeye gidilir; strateji sürümünde tarife.ucret(g) çağrısından
statik olarak üç ayrı gövdeye gidilebilir. Hangisinin çalıştığını anlamak için nesnenin nerede
üretildiğine, yani çağrı zincirinin bir üst basamağına bakmak gerekir. Bu, bir dolaylılık
basamağıdır ve sayısı üçtür.
Denge şöyle kurulur: tarife sayısı arttıkça düzenlenen satır sayısı gömülü sürümde büyür, strateji sürümünde sıfır kalır; dosya sayısı ise gömülü sürümde sabit kalır, strateji sürümünde tarife başına bir artar. İki eğrinin kesiştiği yer kalıbın karşılığını verdiği eşiktir. Tek tarifesi olan bir kitaplıkta strateji kalıbı üç dosya ve bir dolaylılık basamağı ekler, karşılığında sıfır kazanç verir; bu durumun ölçümü konunun son dersindedir.
Özet
- Strateji kalıbı, bir hesabı ayrı bir nesneye alıp çağrı yerine parametre olarak geçirir; ölçülen kazanç, algoritmanın çalışma zamanında değiştirilebilmesidir.
- Aynı veri üzerinde tek koşumda denenebilen algoritma sayısı gömülü sürümde 1, strateji sürümünde 3 çıktı; strateji sürümü standart tarifede birebir aynı tutarları üretti.
- Dördüncü tarife eklendiğinde gömülü sürümde 2 dosya ve 13 satır düzenlendi, strateji sürümünde 0 dosya düzenlendi ve 1 dosya eklendi.
- Bedel iki kalemdir: dosya sayısı 2’den 5’e çıktı ve çağrı yerinden statik olarak izlenmesi gereken olası gövde sayısı 1’den 3’e yükseldi.
- Kalıp, tarife sayısı arttıkça karşılığını verir; tek gerçekleştirimde eklenen dosya ve dolaylılık karşılıksız kalır.
Sonraki Adım
Tarife nesnesi bir hesabı taşıyor ve çağıran onu doğrudan çağırıyor: bağ tek yönlü ve açıktır. Kitaplığın başka bir yerinde ise ilişki ters yönde kuruluyor. Bir teslimatın durumu değiştiğinde günlük satırı yazılması, müşteriye bildirim düşülmesi, ölçüm sayacının artırılması ve faturalama kaydının güncellenmesi gerekiyor. Bu dört işi teslimat modülünün kendisi çağırırsa teslimat dördünü de ithal etmek zorunda kalır ve beşinci iş eklendiğinde yine düzenlenir. Sonraki ders bu durumu giden bağ sayısı ve ithal kapanışı boyutuyla ölçer, sonra bildirimi tersine çevirip aynı sayıları yeniden hesaplar.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.