---
title: 'Şablon Yöntem'
source: 'https://academia.sh/tr/kurslar/tasarim-kaliplari/sablon-yontem'
course: 'Tasarım Kalıpları'
language: tr
updated: '2026-08-17T18:10:38+00:00'
license: 'CC BY-SA 4.0'
---

# Şablon Yöntem

Ücretlendirme adımlarının üç taşıyıcı dosyasında ayrı ayrı sıralanması ile iskeletin tek yere alınmasının karşılaştırılması: en az iki dosyada yinelenen satır sayısı, adım sırası sapmasının ürettiği tutar hatası, yedinci adım eklendiğinde düzenlenen dosya sayısı ve kalıbın bedeli olarak toplam satır artışı ile boş kanca sayısı.

Üç komut aynı üç adımı aynı sırayla yapıyordu: önceki değeri sakla, yeni değeri yaz, kaydı
tut. Aynı tekrar ücretlendirme tarafında daha büyük ölçekte var. Ücret hesabı altı adımdan
geçer — ücretli ağırlığı belirle, taban ücreti hesapla, bölge katsayısını uygula, ek hizmet
ücretini ekle, indirimi düş, asgari ücreti uygula — ve yurt içi, ekspres, yurt dışı taşıyıcıları
için üç dosya bu altı adımı ayrı ayrı sıralar. Adımların **sırası** her üçünde aynı olmak
zorundadır; değişen yalnızca birkaç adımın içeriğidir.

**Şablon yöntem** (template method) sırayı tek bir yere alır ve değişen adımları dışarıdan
gelen kancalara bırakır. Kazanç iki sayıyla ölçülür: yinelenen satır sayısı ve sıraya bir adım
eklendiğinde düzenlenen dosya sayısı. Bir üçüncü sayı da var ve o kalıbın asıl gerekçesi: sıra
bir kopyada saparsa üretilen tutar yanlış olur.

## Yinelenen İskelet

Ortak veri ve bölge katsayıları iki sürümde paylaşılır.

```sh
mkdir -p yinelenen sablon
```

```js
// bolge.mjs — iki surumun paylastigi bolge katsayilari ve gonderi kumesi
export const BOLGE = { "1": 100, "2": 130, "3": 175 };
export const GONDERILER = [
  { kod: "GN-1", agirlik: 2, hacim: 8, bolge: "1", indirim: 0 },
  { kod: "GN-2", agirlik: 6, hacim: 60, bolge: "2", indirim: 10 },
  { kod: "GN-3", agirlik: 14, hacim: 20, bolge: "3", indirim: 0 },
  { kod: "GN-4", agirlik: 1, hacim: 3, bolge: "2", indirim: 40 },
  { kod: "GN-5", agirlik: 22, hacim: 180, bolge: "3", indirim: 25 },
];
```

Yurt içi taşıyıcının dosyası altı adımı sıralar; ek hizmet ücreti sıfır olduğu için o adım
hiç yazılmamış.

```js
// yinelenen/yurtici.mjs — alti adim bu dosyada siralanmis, ek hizmet adimi atlanmis
import { BOLGE } from "../bolge.mjs";

export const ASGARI = 3900;

export function ucretle(gonderi) {
  const agirlik = Math.ceil(gonderi.agirlik);
  let tutar = 2500 + 420 * agirlik;
  tutar = Math.round((tutar * BOLGE[gonderi.bolge]) / 100);
  tutar -= Math.round((tutar * gonderi.indirim) / 100);
  return Math.max(tutar, ASGARI);
}
```

```js
// yinelenen/ekspres.mjs — ayni alti adim yeniden siralanmis
import { BOLGE } from "../bolge.mjs";

export const ASGARI = 6500;

export function ucretle(gonderi) {
  const agirlik = Math.ceil(gonderi.agirlik);
  let tutar = 4000 + 640 * agirlik;
  tutar = Math.round((tutar * BOLGE[gonderi.bolge]) / 100);
  tutar += 1500;
  tutar -= Math.round((tutar * gonderi.indirim) / 100);
  return Math.max(tutar, ASGARI);
}
```

Üçüncü dosyada son iki adım ters sırada: asgari ücret indirimden **önce** uygulanmış.

```js
// yinelenen/yurtdisi.mjs — ayni alti adim, son iki adim ters sirada
import { BOLGE } from "../bolge.mjs";

export const ASGARI = 12000;

export function ucretle(gonderi) {
  const agirlik = Math.max(Math.ceil(gonderi.agirlik), Math.ceil(gonderi.hacim / 5));
  let tutar = 6000 + 900 * agirlik;
  tutar = Math.round((tutar * BOLGE[gonderi.bolge]) / 100);
  tutar += 2200;
  tutar = Math.max(tutar, ASGARI);
  return tutar - Math.round((tutar * gonderi.indirim) / 100);
}
```

## İskeletin Tek Yere Alınması

Şablon sürümünde sıra bir dosyadadır ve değişen dört şey kancadır: ücretli ağırlık, taban
ücret, ek hizmet ve asgari ücret.

```js
// sablon/sablon.mjs — degismeyen iskelet, alti adim tek yerde
import { BOLGE } from "../bolge.mjs";

export function ucretle(tarife, gonderi) {
  const agirlik = tarife.ucretliAgirlik(gonderi);
  let tutar = tarife.tabanUcret(agirlik);
  tutar = Math.round((tutar * BOLGE[gonderi.bolge]) / 100);
  tutar += tarife.ekHizmet(gonderi);
  tutar -= Math.round((tutar * gonderi.indirim) / 100);
  return Math.max(tutar, tarife.asgari);
}
```

```js
// sablon/tarifeler.mjs — yalnizca degisen adimlar
const agirlikca = (g) => Math.ceil(g.agirlik);

export const yurtici = {
  ad: "yurtici",
  asgari: 3900,
  ucretliAgirlik: agirlikca,
  tabanUcret: (a) => 2500 + 420 * a,
  ekHizmet: () => 0,
};

export const ekspres = {
  ad: "ekspres",
  asgari: 6500,
  ucretliAgirlik: agirlikca,
  tabanUcret: (a) => 4000 + 640 * a,
  ekHizmet: () => 1500,
};

export const yurtdisi = {
  ad: "yurtdisi",
  asgari: 12000,
  ucretliAgirlik: (g) => Math.max(Math.ceil(g.agirlik), Math.ceil(g.hacim / 5)),
  tabanUcret: (a) => 6000 + 900 * a,
  ekHizmet: () => 2200,
};
```

Bu, strateji dersindeki tarife nesnesinden bir noktada ayrılır: strateji bütün hesabı
devralıyordu, kanca yalnız bir adımı devralır. Sıranın sahibi iskelettir; kanca ne zaman
çağrıldığını seçemez.

## Sapmanın Ölçülmesi

Sürücü betik on beş hesabı iki sürümde de yapar, sapan sonuçları ve asgari ücretin altına
düşen tutarları sayar.

```js
// kosum.mjs — iki surumu ayni gonderilerle kosar, asgari altina dusen tutari sayar
import { GONDERILER } from "./bolge.mjs";
import * as yurtici from "./yinelenen/yurtici.mjs";
import * as ekspres from "./yinelenen/ekspres.mjs";
import * as yurtdisi from "./yinelenen/yurtdisi.mjs";
import { ucretle } from "./sablon/sablon.mjs";
import * as tarifeler from "./sablon/tarifeler.mjs";

const YINELENEN = { yurtici, ekspres, yurtdisi };
let sapma = 0;
let asgariAlti = { yinelenen: 0, sablon: 0 };

for (const ad of ["yurtici", "ekspres", "yurtdisi"]) {
  const satir = [];
  for (const g of GONDERILER) {
    const a = YINELENEN[ad].ucretle(g);
    const b = ucretle(tarifeler[ad], g);
    if (a !== b) sapma += 1;
    if (a < YINELENEN[ad].ASGARI) asgariAlti.yinelenen += 1;
    if (b < tarifeler[ad].asgari) asgariAlti.sablon += 1;
    satir.push(`${g.kod}=${a}/${b}`);
  }
  console.log(`${ad.padEnd(9)} ${satir.join(" ")}`);
}
console.log(`iki surum arasindaki sapma sayisi = ${sapma}`);
console.log(`asgari ucretin altina dusen tutar: yinelenen=${asgariAlti.yinelenen} sablon=${asgariAlti.sablon}`);
```

```
yurtici   GN-1=3900/3900 GN-2=5873/5873 GN-3=14665/14665 GN-4=3900/3900 GN-5=15409/15409
ekspres   GN-1=6780/6780 GN-2=10523/10523 GN-3=24180/24180 GN-4=6500/6500 GN-5=24855/24855
yurtdisi  GN-1=12000/12000 GN-2=21636/21636 GN-3=34750/34750 GN-4=7200/12000 GN-5=52050/52050
iki surum arasindaki sapma sayisi = 1
asgari ucretin altina dusen tutar: yinelenen=1 sablon=0
```

On beş hesabın on dördü aynı; biri sapıyor. Yüzde kırk indirimli GN-4, yurt dışı taşıyıcıda
yinelenen sürümde 7200, şablon sürümünde 12000. Asgari ücret 12000 olduğu için yinelenen
sürümün ürettiği tutar sözleşmeye aykırıdır. Sapmanın kaynağı bir formül hatası değil, adım
sırasıdır: iskelet üç kez yazıldığında üçüncüsünde iki adım yer değiştirmiştir. Şablon sürümünde
sıra kancalara açık olmadığı için bu sapmanın yazılabileceği bir yer yok.

## Yinelenen Satır ve Yeni Adım

İkinci ölçü statiktir: her sürümde kaç anlamlı satır var ve bunların kaçı en az iki dosyada
birden geçiyor.

```js
// yineleme.mjs — iki surumde yinelenen satir sayisini sayar
import { readFileSync, readdirSync } from "node:fs";

const anlamli = (yol) =>
  readFileSync(yol, "utf8")
    .split("\n")
    .map((s) => s.trim())
    .filter((s) => s.length > 0 && s.startsWith("//") === false && s.startsWith("import ") === false);

function say(dizin) {
  const dosyalar = readdirSync(dizin).sort();
  const sayim = new Map();
  for (const d of dosyalar) {
    for (const s of new Set(anlamli(`${dizin}/${d}`))) sayim.set(s, (sayim.get(s) ?? 0) + 1);
  }
  const yinelenen = [...sayim.entries()].filter(([, n]) => n >= 2);
  const toplam = dosyalar.reduce((t, d) => t + anlamli(`${dizin}/${d}`).length, 0);
  return { dosya: dosyalar.length, toplam, yinelenen: yinelenen.length };
}

for (const dizin of ["yinelenen", "sablon"]) {
  const r = say(dizin);
  console.log(`${dizin.padEnd(10)} dosya=${r.dosya}  anlamli satir=${r.toplam}  en az iki dosyada gecen satir=${r.yinelenen}`);
}
```

```
yinelenen  dosya=3  anlamli satir=26  en az iki dosyada gecen satir=6
sablon     dosya=2  anlamli satir=30  en az iki dosyada gecen satir=0
```

Altıya karşı sıfır. Bu altı satır bilgi yinelemesidir: aynı adımın aynı biçimde üç kez
yazılması. Şimdi sıraya yedinci bir adım giriyor — bölge katsayısından sonra yüzde altı yakıt
ek ücreti.

```js
// adim.mjs — yedinci adimi (yakit ek ucreti) iki surume de ekler
import { cpSync, readFileSync, readdirSync, writeFileSync } from "node:fs";

cpSync("yinelenen", "yinelenen-yeni", { recursive: true });
cpSync("sablon", "sablon-yeni", { recursive: true });

const CAPA = "  tutar = Math.round((tutar * BOLGE[gonderi.bolge]) / 100);";
const YAKIT = "  tutar += Math.round((tutar * 6) / 100);";

const ekle = (yol) => {
  const eski = readFileSync(yol, "utf8");
  const yeni = eski.replace(CAPA, `${CAPA}\n${YAKIT}`);
  writeFileSync(yol, yeni);
  return yeni === eski ? 0 : 1;
};

let yinelenenDuzenlenen = 0;
for (const d of readdirSync("yinelenen-yeni")) yinelenenDuzenlenen += ekle(`yinelenen-yeni/${d}`);
let sablonDuzenlenen = 0;
for (const d of readdirSync("sablon-yeni")) sablonDuzenlenen += ekle(`sablon-yeni/${d}`);

console.log(`yedinci adim icin duzenlenen dosya: yinelenen=${yinelenenDuzenlenen}  sablon=${sablonDuzenlenen}`);

const { GONDERILER } = await import("./bolge.mjs");
const y = await import("./yinelenen-yeni/yurtici.mjs");
const s = await import("./sablon-yeni/sablon.mjs");
const t = await import("./sablon-yeni/tarifeler.mjs");
console.log(`GN-2 yurtici yeni tutar: yinelenen=${y.ucretle(GONDERILER[1])}  sablon=${s.ucretle(t.yurtici, GONDERILER[1])}`);
```

```
yedinci adim icin duzenlenen dosya: yinelenen=3  sablon=1
GN-2 yurtici yeni tutar: yinelenen=6226  sablon=6226
```

Üç dosyaya karşı bir dosya, ve iki sürüm yeni adımdan sonra da aynı tutarı veriyor. Üç dosyanın
düzenlenmesi yalnız iş yükü değil, üç ayrı sapma fırsatıdır; yukarıdaki 7200 tam olarak böyle
oluşmuştur.

## Bedelin Sayımı

Aynı statik ölçüm bedeli de veriyor: anlamlı satır sayısı 26'dan 30'a **yükseldi**. Şablon
yöntem satır sayısını düşürmez; yinelemeyi düşürüp toplamı artırır, çünkü her kanca bir ad ve
bir imza ister. İkinci kalem iş yapmayan kancadır.

```sh
echo "bos adim (is yapmayan kanca) sayisi: yinelenen=$(grep -h 'tutar += 0;' yinelenen/*.mjs | wc -l | tr -d ' ')  sablon=$(grep -h 'ekHizmet: () => 0' sablon/*.mjs | wc -l | tr -d ' ')"
```

```
bos adim (is yapmayan kanca) sayisi: yinelenen=0  sablon=1
```

Yinelenen sürümde yurt içi taşıyıcı ek hizmet adımını hiç yazmamıştı; şablon sürümünde
yazmak zorunda, çünkü iskelet o kancayı çağırıyor. Sıfır döndüren bir kanca, kalıbın istediği
biçimsel bedeldir. Üçüncü kalem okuma maliyetidir: yinelenen sürümde bir taşıyıcının tam akışı
tek dosyada görünür, şablon sürümünde iskelet bir dosyada ve değişen adımlar başka dosyada
durur, yani akışı görmek için iki dosya okunur.

## Özet

- Şablon yöntem değişmeyen adım sırasını tek bir gövdeye alır ve değişen adımları kancalara
  bırakır; kanca ne zaman çağrıldığını seçemez, sıranın sahibi iskelettir.
- En az iki dosyada birden geçen satır sayısı 6'dan 0'a indi; yedinci adım eklendiğinde
  düzenlenen dosya sayısı 3'ten 1'e indi ve iki sürüm aynı tutarı üretmeye devam etti.
- Sıranın bir kopyada sapması on beş hesaptan birinde yanlış tutar üretti: yüzde kırk indirimli
  gönderi yurt dışı taşıyıcıda asgari ücretin altına, 12000 yerine 7200'e düştü.
- Bedel: anlamlı satır sayısı 26'dan 30'a yükseldi ve iş yapmayan kanca sayısı 0'dan 1'e çıktı.
- Bir taşıyıcının tam ücret akışı yinelenen sürümde tek dosyada görünür, şablon sürümünde iki
  dosyaya dağılır.

## Sonraki Adım

Şablonda sıra sabitti; hangi adımın çalışacağı baştan belliydi. Teslimatın kendisinde ise
hangi işlemin geçerli olduğu o anki duruma bağlı. Bir teslimat oluşturulmuş, toplanmış, yolda,
dağıtımda, teslim edilmiş ya da iade edilmiş olabilir; toplanmamış bir gönderi dağıtıma
çıkamaz, teslim edilmiş bir gönderi iade edilemez. Bu kurallar bugün alan üzerinde bayraklarla
ve iç içe koşullarla denetleniyor ve bazı geçersiz geçişler denetimden kaçıyor. Sonraki ders
tüm durum–olay çiftlerini tek tek deneyip kabul edilen geçersiz geçiş sayısını ve çevrimsel
karmaşıklığı ölçer, sonra durumu nesneye çevirip aynı sayıları yeniden hesaplar.
