---
title: Vekil
source: 'https://academia.sh/tr/kurslar/tasarim-kaliplari/vekil'
course: 'Tasarım Kalıpları'
language: tr
updated: '2026-08-17T18:10:39+00:00'
license: 'CC BY-SA 4.0'
---

# Vekil

Erişimin araya girerek denetlenmesi: tarife kaynağına giden çağrı sayısının vekilsiz ve önbellekleyen vekilli bağlamada sayılması, sanal vekille kaynağın ilk isteğe kadar kurulmaması, koruyan vekille nesne düzeyinde araya girme ve bedelin dolaylılık basamağı ile bayat okuma sayısıyla ölçülmesi.

Sinek siklette bir nesne çok sayıda kayıt tarafından paylaşıldı, ama erişim serbestti: kayıt
`tarife` alanını okuyup doğrudan kullanıyordu. Bu dersin problemi erişimin kendisidir.

Tarife tabloları artık kitaplığın içinde değil, ayrı bir kaynakta duruyor ve o kaynağa gitmek
pahalıdır. Üç istek birden çıkıyor: kaynak hiç kullanılmayacaksa hiç kurulmasın, aynı tablo
ikinci kez istendiğinde tekrar getirilmesin, belli tarifelere erişim kapatılabilsin. Üçünün de
ortak yanı, asıl nesnenin gövdesine dokunmamaktır. **Vekil** (proxy) asıl nesneyle **aynı
arayüzü** sunan ve çağrıyı ona geçirirken araya giren bir nesnedir. Ölçü kaynağa giden çağrı
sayısıdır; bedel dolaylılık basamağı ve bayat okuma sayısıyla ölçülür.

## Problem: Her Kayıt Kaynağa Gidiyor

Kaynak, kuruluşunu ve her çağrısını sayıyor. Sayaçlar ölçümün aracıdır; gerçek bir kaynakta
bu sayılar ağ isteği ya da dosya okuması demektir.

```js
// kaynak/tarife-kaynagi.mjs — gercek kaynak: kurulusu ve her cagrisi sayilir
const TABLOLAR = {
  standart: { kademeler: [[1, 4990], [5, 8490], [15, 14990], [30, 24990]], katsayi: { 34: 100, "06": 118, 35: 120, 65: 145 } },
  ekonomik: { kademeler: [[1, 3990], [5, 6990], [15, 11990], [30, 19990]], katsayi: { 34: 100, "06": 118, 35: 120, 65: 145 } },
  hizli: { kademeler: [[1, 6490], [5, 10990], [15, 18990], [30, 31990]], katsayi: { 34: 100, "06": 118, 35: 120, 65: 145 } },
};

let kurulan = 0, cagri = 0;

export const sayaclar = () => ({ kurulan, cagri });
export const sayaclariSifirla = () => { kurulan = 0; cagri = 0; };

export function kaynakKur() {
  kurulan += 1;
  return {
    getir(kod) {
      cagri += 1;
      const t = TABLOLAR[kod];
      if (t === undefined) throw new RangeError(`bilinmeyen tarife: ${kod}`);
      return t;
    },
  };
}

export function kaynaktaGuncelle(kod, zamKatsayisi) {
  TABLOLAR[kod] = {
    kademeler: TABLOLAR[kod].kademeler.map(([u, f]) => [u, Math.round(f * zamKatsayisi)]),
    katsayi: TABLOLAR[kod].katsayi,
  };
}
```

```js
// veri.mjs — ham gonderi satirlari; degerler dizinden turetilir, rastgelelik yok
const POSTA = ["34100", "06500", "35400", "65100"];
const TARIFE = ["standart", "ekonomik", "hizli"];

export function* hamSatirlar(n) {
  for (let i = 0; i < n; i += 1) {
    yield {
      kod: `G-${i}`,
      agirlik: 0.5 + (i % 59) / 2,
      postaKodu: POSTA[i % POSTA.length],
      tarifeKodu: TARIFE[i % TARIFE.length],
    };
  }
}
```

İstemci kaynağı dışarıdan alır ve türünü bilmez. Bu dersin bütün ölçümlerinde **aynı dosya**
kullanılır; değişen tek şey ona hangi nesnenin geçirildiğidir.

```js
// istemci.mjs — iki baglamada da ayni dosya: kaynagi disaridan alir, turunu bilmez
export function ucretlendir(kaynak, satirlar) {
  let toplam = 0;
  for (const s of satirlar) {
    const t = kaynak.getir(s.tarifeKodu);
    const k = t.kademeler.find(([ust]) => s.agirlik <= ust) ?? [0, 24990];
    toplam += Math.round((k[1] * (t.katsayi[s.postaKodu.slice(0, 2)] ?? 165)) / 100);
  }
  return toplam;
}
```

## Çözüm: Aynı Arayüzü Sunan Üç Vekil

Önbellekleyen vekil, aynı kod için kaynağa bir kez gider. Arayüzü kaynağınkiyle aynıdır:
`getir(kod)`.

```js
// vekil/onbellek.mjs — onbellekleyen vekil: ayni arayuz, ayni kod ikinci kez kaynaga gitmez
export const onbellekVekil = (ic) => {
  const bellek = new Map();
  return {
    ic,
    getir(kod) {
      if (!bellek.has(kod)) bellek.set(kod, ic.getir(kod));
      return bellek.get(kod);
    },
    bellekBoyu: () => bellek.size,
  };
};
```

```js
// vekil/tembel.mjs — sanal vekil: kaynak ilk istege kadar kurulmaz
export const tembelVekil = (kaynakKurucu) => {
  let ic = null;
  return {
    get ic() { return ic ?? undefined; },
    getir(kod) {
      ic ??= kaynakKurucu();
      return ic.getir(kod);
    },
  };
};
```

```js
// vekil/koruma.mjs — koruyan vekil: nesne duzeyinde araya girer, izinli kod listesine bakar
export const korumaVekili = (ic, izinli) => ({
  ic,
  getir(kod) {
    if (!izinli.includes(kod)) throw new RangeError(`erisim kapali: ${kod}`);
    return ic.getir(kod);
  },
});
```

Koruyan vekilin yaptığı iş **nesne düzeyinde araya girmedir**: hangi nesnenin hangi çağrıyı
geçireceğine karar verir. Kimin hangi tarifeye erişebileceğini belirleyen kurallar — kimlik,
rol, izin modeli — Kimlik Doğrulama ve Yetkilendirme kursunda ele alınmıştır; buradaki liste o
kararın sonucunu taşıyan bir veridir, kuralın kendisi değil.

## Çağrı Sayısının Ölçülmesi

```js
// kosum.mjs — vekilsiz ve onbellekli baglama: kaynaga giden cagri sayisi ve toplam ucret
import { hamSatirlar } from "./veri.mjs";
import { kaynakKur, sayaclar, sayaclariSifirla } from "./kaynak/tarife-kaynagi.mjs";
import { onbellekVekil } from "./vekil/onbellek.mjs";
import { ucretlendir } from "./istemci.mjs";

const N = 20000;
const derinlik = (n) => (n?.ic === undefined ? 1 : 1 + derinlik(n.ic));

sayaclariSifirla();
const dogrudanKaynak = kaynakKur();
const a = ucretlendir(dogrudanKaynak, hamSatirlar(N));
const s1 = sayaclar();

sayaclariSifirla();
const vekilliKaynak = onbellekVekil(kaynakKur());
const b = ucretlendir(vekilliKaynak, hamSatirlar(N));
const s2 = sayaclar();

console.log(`vekilsiz: toplam=${a}  kaynak cagrisi=${s1.cagri}  derinlik=${derinlik(dogrudanKaynak)}`);
console.log(`vekilli : toplam=${b}  kaynak cagrisi=${s2.cagri}  derinlik=${derinlik(vekilliKaynak)}  ` +
  `onbellek=${vekilliKaynak.bellekBoyu()}`);
console.log(`toplam farki = ${a - b}  gonderi = ${N}`);
```

```
vekilsiz: toplam=463093345  kaynak cagrisi=20000  derinlik=1
vekilli : toplam=463093345  kaynak cagrisi=3  derinlik=2  onbellek=3
toplam farki = 0  gonderi = 20000
```

Kaynağa giden çağrı 20.000'den 3'e indi; toplam ücret kuruşu kuruşuna aynı. Üç sayı, üç
farklı tarife koduna karşılık gelir: önbellek boyu iç durum çeşidi kadardır. Kazancın
tamamı istemci dosyasında **tek satır** değişmeden elde edildi; iki koşumda `istemci.mjs`
aynı dosyadır ve `getir` çağrısı da aynıdır. Değişen şey yalnız bileşim kökünde hangi nesnenin
geçirildiğidir. Vekilin arayüzü kaynağınkiyle aynı olmasaydı bu mümkün olmazdı — kalıbın
tek zorunlu kuralı budur.

## Üç Vekilin Üst Üste Konması

Vekiller de aynı arayüzü döndürdüğü için birbirini sarabilir. Aşağıdaki zincir dıştan içe
koruma, önbellek ve sanal vekildir; en içte kaynağın kendisi vardır ve daha kurulmamıştır.

```js
// zincir.mjs — uc vekil ust uste: koruma -> onbellek -> tembel kaynak
import { hamSatirlar } from "./veri.mjs";
import { kaynakKur, sayaclar, sayaclariSifirla } from "./kaynak/tarife-kaynagi.mjs";
import { onbellekVekil } from "./vekil/onbellek.mjs";
import { tembelVekil } from "./vekil/tembel.mjs";
import { korumaVekili } from "./vekil/koruma.mjs";
import { ucretlendir } from "./istemci.mjs";

const derinlik = (n) => (n?.ic === undefined ? 1 : 1 + derinlik(n.ic));

sayaclariSifirla();
const zincir = korumaVekili(onbellekVekil(tembelVekil(kaynakKur)), ["standart", "ekonomik", "hizli"]);
console.log(`ilk cagri oncesi: kaynak kurulan=${sayaclar().kurulan}  derinlik=${derinlik(zincir)}`);

const toplam = ucretlendir(zincir, hamSatirlar(20000));
console.log(`ilk cagri sonrasi: kaynak kurulan=${sayaclar().kurulan}  cagri=${sayaclar().cagri}  ` +
  `derinlik=${derinlik(zincir)}  toplam=${toplam}`);

const kapali = korumaVekili(onbellekVekil(tembelVekil(kaynakKur)), ["standart"]);
try {
  ucretlendir(kapali, hamSatirlar(3));
  console.log("kapali zincir: butun kodlar gecti");
} catch (e) {
  console.log(`kapali zincir: ${e.message}`);
}
```

```
ilk cagri oncesi: kaynak kurulan=0  derinlik=3
ilk cagri sonrasi: kaynak kurulan=1  cagri=3  derinlik=4  toplam=463093345
kapali zincir: erisim kapali: ekonomik
```

İlk çağrıdan önce kaynak **hiç kurulmadı**: sayaç 0. Zincir derinliği 3'ken ilk çağrıdan
sonra 4 oldu, çünkü kaynak o anda kurulup zincire katıldı. Toplam ücret tek vekilli koşumla
aynıdır. İzinli listesi daraltıldığında ikinci tarife kodunda erişim kapandı ve hata istemciye
kadar çıktı; koruyan vekil kararı kaynağa hiç gitmeden verdi.

## Bedel: Dolaylılık ve Bayat Okuma

Zincir derinliği ölçümü bedelin ilk yarısıdır: bir tablo isteği için izlenecek gövde sayısı
1'den 4'e çıktı. İkinci yarısı önbelleğin doğasındadır — kaynak değişirse vekil eski değeri
vermeye devam eder.

```js
// bayat.mjs — onbellegin bedeli: kaynak degisince vekil eski tabloyu vermeye devam eder
import { hamSatirlar } from "./veri.mjs";
import { kaynakKur, kaynaktaGuncelle, sayaclariSifirla } from "./kaynak/tarife-kaynagi.mjs";
import { onbellekVekil } from "./vekil/onbellek.mjs";
import { ucretlendir } from "./istemci.mjs";

const N = 20000;
sayaclariSifirla();
const vekilli = onbellekVekil(kaynakKur());
const dogrudan = kaynakKur();

console.log(`zam oncesi : vekilli=${ucretlendir(vekilli, hamSatirlar(N))}  ` +
  `dogrudan=${ucretlendir(dogrudan, hamSatirlar(N))}`);

kaynaktaGuncelle("standart", 1.1);

const sonraVekilli = ucretlendir(vekilli, hamSatirlar(N));
const sonraDogrudan = ucretlendir(dogrudan, hamSatirlar(N));
const etkilenen = [...hamSatirlar(N)].filter((s) => s.tarifeKodu === "standart").length;
console.log(`zam sonrasi: vekilli=${sonraVekilli}  dogrudan=${sonraDogrudan}`);
console.log(`bayat okuma = ${etkilenen} gonderi, eksik hesaplanan = ${sonraDogrudan - sonraVekilli} kurus`);
```

```
zam oncesi : vekilli=463093345  dogrudan=463093345
zam sonrasi: vekilli=463093345  dogrudan=478135357
bayat okuma = 6667 gonderi, eksik hesaplanan = 15042012 kurus
```

Standart tarifeye yüzde on zam geldikten sonra vekilli hesap eski tabloyu kullandı: 6667
gönderi bayat okundu ve toplam 15.042.012 kuruş eksik hesaplandı. Hiçbir hata atılmadı. Kazanç
ile bedel aynı mekanizmadan doğuyor: çağrı sayısını 20.000'den 3'e indiren şey, tazeliği 3
okumaya bağlayan şeydir. Önbellekleyen vekil kullanılıyorsa geçerlilik süresi ya da geçersiz
kılma yolu tasarımın parçasıdır; yoksa kalıp doğruluğu sessizce bozar.

Vekilin dekoratörden ayrımı da burada nettir. İkisi de aynı arayüzü korur ve araya girer, ama
dekoratör sonucun üstüne bir **davranış** ekler — yakıt farkı, vergi — ve zincirin sırası
sonucu değiştirir. Vekil sonucu değiştirmez; **erişimi** yönetir: ne zaman gidileceğine, kaç
kez gidileceğine, gidilip gidilmeyeceğine karar verir. Bu yüzden vekilli ve vekilsiz koşumların
toplamı aynı çıkmak zorundadır — çıkmadığı yer, yukarıdaki bayatlık ölçümünde olduğu gibi,
bir kusurun kanıtıdır.

İki durumda uygulanmaz. Erişim ucuzsa ve tekrar yoksa — tablo bellekte duruyorsa, her kod bir
kez isteniyorsa — önbellekleyen vekil yalnız bir eşleme ve bir dolaylılık basamağı ekler.
Kaynağın taze olması zorunluysa ve geçersiz kılma yolu yoksa önbellek yasaktır; o durumda
vekilden beklenen kazanç, ölçülemeyen bir doğruluk riskiyle ödenir.

## Özet

- Vekilin çözdüğü problem, asıl nesnenin gövdesine dokunmadan erişimi denetlemektir; zorunlu
  kural vekilin asıl nesneyle aynı arayüzü sunmasıdır.
- Önbellekleyen vekil kaynağa giden çağrıyı 20.000'den 3'e indirdi; toplam ücret aynı kaldı ve
  istemci dosyasında tek satır değişmedi — değişen yer bileşim köküdür.
- Sanal vekil kaynağı ilk isteğe kadar hiç kurmadı (kuruluş sayacı 0), koruyan vekil kapalı
  bir tarife kodunda kararı kaynağa gitmeden verdi.
- Bedel iki sayıyla ölçüldü: bir istek için izlenecek gövde sayısı 1'den 4'e çıktı ve kaynak
  değiştiğinde 6667 gönderi bayat okuyup 15.042.012 kuruş eksik hesaplandı.
- Vekil erişimi yönetir, dekoratör davranış ekler: vekilli ve vekilsiz koşumun sonucu aynı
  olmak zorundadır. Erişim ucuzsa ya da tazelik zorunluysa kalıp karşılığını vermez.

## Sonraki Adım

Yapısal kalıplar nesnelerin nasıl birleştirileceğini düzenledi: bağdaştırıcı uyumsuz iki
arayüzü aynı sözleşmeye çevirdi, köprü iki bağımsız ekseni ayırdı, bileşik ağacı tek biçim ele
aldı, dekoratör davranışı katmanladı, cephe alt sistemin önüne tek kapı koydu, sinek siklet
değişmeyeni paylaştı, vekil ise araya girip çağrıyı **aynı arayüzle** ilerisine geçirdi. Bu
son cümlede bir sınır saklı: vekilde araya giren nesnenin işi çağrıyı geçirmekti, hesabın
kendisi hiç değişmiyordu — zaten değişmemesi gerekiyordu.

Sıradaki soru birleşme değil, **davranışın seçilmesi**: aynı arayüzün arkasındaki algoritma
çalışma zamanında değiştirilebilir mi? Ücretlendirme kitaplığında bu somut bir istektir — aynı
gönderi kümesinin standart tarifeyle, ekspres tarifeyle ve hacimden türetilen ücretli ağırlık
tarifesiyle aynı koşumda fiyatlanması. Sonraki ders algoritmayı bir nesneye alıp çağrı yerine
parametre olarak geçiren strateji kalıbını yazar ve tek koşumda yan yana durabilen algoritma
sayısıyla yeni algoritma eklendiğinde düzenlenen dosya sayısını ölçer.
