---
title: 'Arabulucu ve Anımsatıcı'
source: 'https://academia.sh/tr/kurslar/tasarim-kaliplari/arabulucu-ve-animsatici'
course: 'Tasarım Kalıpları'
language: tr
updated: '2026-08-17T18:10:38+00:00'
license: 'CC BY-SA 4.0'
---

# Arabulucu ve Anımsatıcı

Operatör formundaki dört alanın birbirini doğrudan tanıması ile yönlendirmenin tek nesnede toplanmasının karşılaştırılması: alanlar arası bağ sayısı, güncelleme çağrısı sayısı, bağ sayısının alan sayısıyla büyümesi; ayrıca durum saklamanın dışarıdan erişilen iç alan sayısı ve fişin taşıdığı alan sayısıyla ölçülmesi.

Ziyaretçi bir işlemi ağacın her düğümüne uyguluyordu; düğümler birbirini tanımıyordu. Operatör
ekranında ise nesneler birbirini doğrudan tanıyor. Taşıyıcı seçimi, tarife özeti, indirim kutusu
ve teslim tarihi alanı birbirini haberdar ediyor: taşıyıcı değişince tarife yenileniyor, tarife
değişince indirim yeniden hesaplanıyor, indirim değişince teslim tarihi güncelleniyor. Her alan
kendisinden sonra etkilenen alanları tanıyor ve doğrudan çağırıyor.

Bu dersin iki kalıbı iki ayrı sorunu hedefler. **Arabulucu** (mediator) alanlar arasındaki
yönlendirmeyi tek bir nesneye toplar; alanlar birbirini tanımaz. **Anımsatıcı** (memento) bir
nesnenin durumunu, iç alanlarını dışarıya açmadan saklanabilir bir fişe çevirir. Ölçülecek
sayılar alanlar arası bağ sayısı, güncelleme çağrısı sayısı, dışarıdan erişilen iç alan sayısı
ve fişin taşıdığı alan sayısıdır.

## Alanların Birbirini Tanıması

```sh
mkdir -p dogrudan arabulucu animsatici
```

```js
// dogrudan/form.mjs — her alan kendisinden sonra etkilenen alanlari dogrudan tanir
const TARIFE = { yurtici: 8400, ekspres: 12600, yurtdisi: 21000 };

export function form() {
  const d = { tasiyici: "yurtici", tarife: 0, indirim: 0, teslimGun: 0 };
  let cagri = 0;

  const teslimTarihi = {
    guncelle() {
      cagri += 1;
      d.teslimGun = (d.tasiyici === "ekspres" ? 1 : 3) + (d.indirim >= 10 ? 1 : 0);
    },
  };
  const indirim = {
    guncelle() {
      cagri += 1;
      d.indirim = d.tarife > 10000 ? 10 : 5;
      teslimTarihi.guncelle();
    },
  };
  const tarife = {
    guncelle() {
      cagri += 1;
      d.tarife = TARIFE[d.tasiyici];
      indirim.guncelle();
      teslimTarihi.guncelle();
    },
  };
  const tasiyici = {
    sec(ad) {
      cagri += 1;
      d.tasiyici = ad;
      tarife.guncelle();
      indirim.guncelle();
      teslimTarihi.guncelle();
    },
  };
  return { deger: d, sec: (ad) => tasiyici.sec(ad), sayac: () => cagri };
}
```

Her alan, kendisinden sonra etkilenebilecek her alanı çağırıyor. Bu, gözlemci dersindekinden
farklı bir durumdur: orada haber veren dinleyicilerini tanımıyordu, burada her alan hem haber
veriyor hem haber alıyor ve bağlar iki yönlü.

## Yönlendirmenin Tek Nesnede Toplanması

Arabulucu sürümünde alanlar birbirini hiç görmez; her biri yalnız kendi hesabını yapar.

```js
// arabulucu/alanlar.mjs — hicbir alan digerini tanimaz, yalniz kendi isini yapar
const TARIFE = { yurtici: 8400, ekspres: 12600, yurtdisi: 21000 };

export const alanlar = (say) => ({
  tarife: {
    guncelle(d) {
      say();
      d.tarife = TARIFE[d.tasiyici];
    },
  },
  indirim: {
    guncelle(d) {
      say();
      d.indirim = d.tarife > 10000 ? 10 : 5;
    },
  },
  teslimTarihi: {
    guncelle(d) {
      say();
      d.teslimGun = (d.tasiyici === "ekspres" ? 1 : 3) + (d.indirim >= 10 ? 1 : 0);
    },
  },
});
```

```js
// arabulucu/form.mjs — alanlar arasindaki yonlendirme yalniz burada
import { alanlar } from "./alanlar.mjs";

const SIRA = ["tarife", "indirim", "teslimTarihi"];

export function form() {
  const d = { tasiyici: "yurtici", tarife: 0, indirim: 0, teslimGun: 0 };
  let cagri = 0;
  const say = () => {
    cagri += 1;
  };
  const a = alanlar(say);
  const bildir = (kaynak) => {
    const bas = kaynak === "tasiyici" ? 0 : SIRA.indexOf(kaynak) + 1;
    for (const ad of SIRA.slice(bas)) a[ad].guncelle(d);
  };
  return {
    deger: d,
    sec(ad) {
      say();
      d.tasiyici = ad;
      bildir("tasiyici");
    },
    sayac: () => cagri,
  };
}
```

## Değerlerin Eşitliği ve Çağrı Sayısı

```js
// kosum.mjs — iki surumu ayni secimlerle kosar, deger ve cagri sayisini karsilastirir
import { form as dogrudanForm } from "./dogrudan/form.mjs";
import { form as arabulucuForm } from "./arabulucu/form.mjs";

let sapma = 0;
for (const secim of ["ekspres", "yurtdisi", "yurtici"]) {
  const a = dogrudanForm();
  const b = arabulucuForm();
  a.sec(secim);
  b.sec(secim);
  const ozet = (f) => Object.entries(f.deger).map(([k, v]) => `${k}=${v}`).join(" ");
  if (ozet(a) !== ozet(b)) sapma += 1;
  console.log(`${secim.padEnd(9)} ${ozet(a)}  |  guncelleme cagrisi dogrudan=${a.sayac()} arabulucu=${b.sayac()}`);
}
console.log(`iki surum arasindaki deger sapmasi = ${sapma}`);
```

```
ekspres   tasiyici=ekspres tarife=12600 indirim=10 teslimGun=2  |  guncelleme cagrisi dogrudan=8 arabulucu=4
yurtdisi  tasiyici=yurtdisi tarife=21000 indirim=10 teslimGun=4  |  guncelleme cagrisi dogrudan=8 arabulucu=4
yurtici   tasiyici=yurtici tarife=8400 indirim=5 teslimGun=3  |  guncelleme cagrisi dogrudan=8 arabulucu=4
iki surum arasindaki deger sapmasi = 0
```

Değerler birebir aynı, güncelleme çağrısı sayısı sekize karşı dört. Fazla dört çağrı yinelenen
tetiklemedir: teslim tarihi bir seçim başına üç kez hesaplanıyor, çünkü onu üç ayrı alan çağırıyor.
İlk iki hesabın sonucu üçüncüsü tarafından ezildiği için değer doğru çıkıyor — bu, sırf şansa
bağlı bir doğruluktur. Alan sayısı arttıkça yinelenen tetikleme sayısı da artar ve sıra
garantisi kaybolur.

## Bağ Sayısının Büyümesi

```js
// bag.mjs — alanlar arasindaki bag sayisi ve alan sayisiyla buyumesi
import { readFileSync } from "node:fs";

const dogrudanMetin = readFileSync("dogrudan/form.mjs", "utf8");
const arabulucuMetin = readFileSync("arabulucu/form.mjs", "utf8");

const kardesBag = (dogrudanMetin.match(/\b(tarife|indirim|teslimTarihi)\.guncelle\(\)/g) ?? []).length;
const arabulucuBag = (arabulucuMetin.match(/^const SIRA = \[(.*)\];$/m)[1].split(",")).length;

console.log(`dogrudan  alanlar arasi bag=${kardesBag}  arabulucu nesnesi=0`);
console.log(`arabulucu alanlar arasi bag=0  arabulucunun tanidigi alan=${arabulucuBag}`);
console.log(`N=4 icin formul denetimi: N(N-1)/2=${(4 * 3) / 2}  N-1=${4 - 1}`);
for (const n of [4, 6, 8]) {
  console.log(`N=${n}  dogrudan bag=${(n * (n - 1)) / 2}  arabulucu bag=${n - 1}`);
}
```

```
dogrudan  alanlar arasi bag=6  arabulucu nesnesi=0
arabulucu alanlar arasi bag=0  arabulucunun tanidigi alan=3
N=4 icin formul denetimi: N(N-1)/2=6  N-1=3
N=4  dogrudan bag=6  arabulucu bag=3
N=6  dogrudan bag=15  arabulucu bag=5
N=8  dogrudan bag=28  arabulucu bag=7
```

Ölçülen altı bağ, dört alanlı tam zincir için $N(N-1)/2$ formülünün verdiği sayıya eşit;
arabulucu sürümünde ölçülen üç bağ $N-1$ formülünü doğruluyor. İki formülün büyüme hızı farklı:
sekiz alanda yirmi sekize karşı yedi. Bağ sayısındaki bu düşüş bedelsiz değildir. Arabulucu bir
nesne ve bir dosya ekler; daha önemlisi, bilgi alanlardan çıkıp arabulucuda birikir. `SIRA`
dizisi artık formun tüm bağımlılık sırasını taşıyan tek yerdir ve alan sayısı arttıkça bu tek
dosyanın değişim nedeni sayısı da artar. Arabulucunun büyümesi, kalıbın bilinen sonucudur:
bağlar azalır, merkezdeki nesne şişer.

## Durumun Saklanması

Operatör bir düzenleme oturumunda birkaç deneme yapıp beğenmezse başlangıca dönmek istiyor.
Birinci yol, çağıranın alanları kendisi okuyup geri yazmasıdır.

```js
// dogrudan/oturum.mjs — cagiran ic alanlari kendisi okuyup geri yaziyor
export const kopyaAl = (f) => ({
  tasiyici: f.deger.tasiyici,
  tarife: f.deger.tarife,
  indirim: f.deger.indirim,
  teslimGun: f.deger.teslimGun,
});

export const kopyaYaz = (f, k) => {
  f.deger.tasiyici = k.tasiyici;
  f.deger.tarife = k.tarife;
  f.deger.indirim = k.indirim;
  f.deger.teslimGun = k.teslimGun;
};
```

Anımsatıcı sürümünde durum hiç dışarı çıkmaz. Nesne bir fiş verir; fiş yalnızca bir sıra
numarası taşır ve içeriği dışarıdan okunamaz.

```js
// animsatici/oturum.mjs — durum disariya acilmadan saklanir, fis yalniz bir sira numarasi tasir
export function oturum(baslangic) {
  let durum = { ...baslangic };
  const kayitlar = [];
  return {
    yaz(ad, deger) {
      durum[ad] = deger;
    },
    animsat() {
      kayitlar.push({ ...durum });
      return { no: kayitlar.length - 1 };
    },
    geriYukle(fis) {
      if (fis.no in kayitlar === false) return false;
      durum = { ...kayitlar[fis.no] };
      return true;
    },
    ozet: () => Object.entries(durum).map(([k, v]) => `${k}=${v}`).join(" "),
  };
}
```

```js
// saklama.mjs — iki saklama yontemini karsilastirir
import { readFileSync } from "node:fs";
import { form } from "./arabulucu/form.mjs";
import { kopyaAl, kopyaYaz } from "./dogrudan/oturum.mjs";
import { oturum } from "./animsatici/oturum.mjs";

const ozet = (d) => Object.entries(d).map(([k, v]) => `${k}=${v}`).join(" ");

const f = form();
f.sec("yurtici");
const fis1 = kopyaAl(f);
f.sec("yurtdisi");
const bozuk = ozet(f.deger);
kopyaYaz(f, fis1);
console.log(`dogrudan  fis alan sayisi=${Object.keys(fis1).length}  geri yukleme sonrasi: ${ozet(f.deger)}`);

const o = oturum({ tasiyici: "yurtici", tarife: 8400, indirim: 5, teslimGun: 3 });
const fis2 = o.animsat();
o.yaz("tasiyici", "yurtdisi");
o.yaz("tarife", 21000);
o.yaz("indirim", 10);
o.yaz("teslimGun", 4);
const yuklendi = o.geriYukle(fis2);
console.log(`animsatici fis alan sayisi=${Object.keys(fis2).length}  geri yukleme=${yuklendi ? 1 : 0}  sonrasi: ${o.ozet()}`);
console.log(`degistirilmis ara durum: ${bozuk}`);

const disErisim = (yol) => (readFileSync(yol, "utf8").match(/f\.deger\.\w+/g) ?? []).length;
console.log(`disaridan erisilen ic alan: dogrudan=${disErisim("dogrudan/oturum.mjs")}  animsatici=${disErisim("animsatici/oturum.mjs")}`);
console.log(`gecersiz fis denemesi: animsatici geriYukle({ no: 9 })=${o.geriYukle({ no: 9 }) ? 1 : 0}`);
```

```
dogrudan  fis alan sayisi=4  geri yukleme sonrasi: tasiyici=yurtici tarife=8400 indirim=5 teslimGun=3
animsatici fis alan sayisi=1  geri yukleme=1  sonrasi: tasiyici=yurtici tarife=8400 indirim=5 teslimGun=3
degistirilmis ara durum: tasiyici=yurtdisi tarife=21000 indirim=10 teslimGun=4
disaridan erisilen ic alan: dogrudan=8  animsatici=0
gecersiz fis denemesi: animsatici geriYukle({ no: 9 })=0
```

İki yöntem de aynı başlangıç durumunu geri getirdi; ara durumun gerçekten değiştiği üçüncü
satırda görülüyor. Fark ölçüde: dışarıdan erişilen iç alan sayısı sekize karşı sıfır, fişin
taşıdığı alan sayısı dörde karşı bir. Birinci yöntem, alan başına bir okuma ve bir yazma
gerektirdiği için beşinci bir alan eklendiğinde iki satır daha büyür; anımsatıcı sürümünde
`{ ...durum }` alan sayısından bağımsızdır ve düzenlenmez.

Bedel iki yerde. Birincisi bellektir: her fiş durumun tam bir kopyasını saklar, kayıt sayısı
kadar kopya birikir ve bunları temizleyecek bir kural yoktur. İkincisi fişin geçerliliğidir:
son satır, var olmayan bir numarayla yapılan geri yükleme denemesinin sessizce başarısız
olduğunu gösteriyor. Fiş içeriğini gizlediği için çağıran fişin hâlâ anlamlı olup olmadığını
sınayamaz; kalıp bu sınamayı nesnenin kendisine bırakır.

## Özet

- Arabulucu alanlar arasındaki yönlendirmeyi tek nesneye toplar; alanlar birbirini tanımaz,
  yalnız kendi hesabını yapar.
- Değerler iki sürümde birebir aynı çıktı; güncelleme çağrısı sayısı 8'den 4'e indi, fazladan
  dört çağrı yinelenen tetiklemeydi.
- Alanlar arası bağ sayısı 6'dan 3'e indi; ölçüm $N(N-1)/2$ ve $N-1$ formüllerini doğruladı,
  sekiz alanda fark 28'e karşı 7 oluyor. Bedel, bağımlılık sırasını tek başına taşıyan bir
  arabulucu nesnesidir.
- Anımsatıcı durumu dışarı açmadan saklar: dışarıdan erişilen iç alan sayısı 8'den 0'a,
  fişin taşıdığı alan sayısı 4'ten 1'e indi ve iki yöntem aynı başlangıç durumunu geri getirdi.
- Anımsatıcının bedeli her fişin tam bir durum kopyası tutması ve geçersiz bir fişin sessizce
  başarısız olmasıdır.

## Sonraki Adım

Arabulucu bir sırayı, anımsatıcı bir kopyayı saklıyor; ikisi de veri tutuyor ama veri hâlâ
kodun içinde yazılı. Tarife kuralları tarafında bunun sınırına gelindi: her yeni koşullu kural
bir dosya değişikliği demek ve kuralları yazan kişi geliştirici değil, tarife birimi. Kuralın
metin olarak yazılıp çalışma zamanında yorumlanması isteniyor. Aynı bölümde ikinci bir soru var:
bir gönderiye hiçbir indirim uygulanmadığında sonucun `null` olmasıyla, hiçbir şey yapmayan bir
indirim nesnesi olması arasındaki fark nedir. Sonraki ders küçük bir kural dilini yorumlayan
nesne ağacını kurar, yeni kural eklendiğinde düzenlenen dosya sayısını ölçer ve boş değer
denetimi sayısını boş nesneyle karşılaştırır.
