İçeriğe geç
academia.sh

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.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat