---
title: 'Neden Çerçeve'
source: 'https://academia.sh/tr/kurslar/bilesen-tabanli-gelistirme/neden-cerceve'
course: 'Bileşen Tabanlı Arayüz Geliştirme'
language: tr
updated: '2026-08-17T18:09:13+00:00'
license: 'CC BY-SA 4.0'
---

# Neden Çerçeve

Arayüzü elle güncellemenin ölçek sorunları; güncelleme yollarının sayısı, durumun iki kopyada tutulması, kaymanın kendini onaran biçimi ve bir arayüz çerçevesinin devraldığı iş.

Tarayıcı ve Web Platformu kursu istasyon sayfasını programın erişebildiği bir veri
yapısına dönüştürdü: düğüm oluşturuldu, dinleyici bağlandı, değişiklik ilgili ögeye
yazıldı, sökülürken hepsi geri alındı. Ölçüm rozeti bir bileşene dönüştü, ama panodaki on
beş rozetin ölçüm listesiyle eşzamanlı kalması hâlâ elle yazılmış güncelleme koduna bağlı.
Bir kayıt eklendiğinde hangi düğümlerin değişeceğini kodun kendisi hesaplıyor.

Bu ders o hesabın neden büyüdükçe hata kaynağına dönüştüğünü ölçer. Sorunun adı beceriksiz
kod değildir; **buyurgan (imperative)** güncellemenin yapısal bir özelliğidir ve pano
büyüdükçe kaçınılmaz hâle gelir.

## Elle Güncellemenin Üç İşi

Arayüzün her parçası için üç ayrı kod yolu yazılır.

**İlk oluşturma** düğümleri kurar, özniteliklerini yazar, dinleyicileri bağlar.
**Güncelleme**, durum değiştiğinde var olan düğümlerin hangi alanlarının yeni değeri
alacağını belirler. **Sökme**, parça kaldırılırken dinleyicileri kaldırır, zamanlayıcıları
durdurur, gözlemcileri çözer.

Üçü de aynı görünümü betimler ama üçü ayrı yerde yazılıdır. Doğruluk koşulu şudur: ilk
oluşturmanın ürettiği ağaç ile, aynı duruma güncelleme yoluyla ulaşılmış ağaç birbirinin
aynısı olmalıdır. Bu koşulu hiçbir şey denetlemez. İlk oluşturmaya bir sınıf eklenip
güncellemeye eklenmediğinde sayfa ilk yüklemede doğru, ilk etkileşimden sonra yanlış olur.

## Güncelleme Yollarının Sayısı

Güncelleme kodunun büyüklüğü parça sayısıyla değil, **durum alanı ile parça arasındaki
bağlantı sayısıyla** ölçülür. Bir alan kaç parçayı besliyorsa, o alanı değiştiren her
işleyicinin o kadar yeri güncellemesi gerekir.

```js
// elle-guncelleme.mjs — durum alani ile arayuz parcasi arasindaki guncelleme yollari
// Panodaki her arayuz parcasinin hangi durum alanlarini okudugunu bildiriyoruz.
const PANO = {
  "rozet.deger": ["olcumler"],
  "rozet.renk": ["olcumler", "esikler"],
  "tablo.satirlar": ["olcumler", "suzgec"],
  "tablo.sayac": ["olcumler", "suzgec"],
  "suzgec.secim": ["suzgec"],
  "baslik.zaman": ["sonGuncelleme"],
};

// Bir durum alani degistiginde elle yazilmasi gereken guncelleme yollari:
// o alani okuyan her parca icin bir yol.
function ozet(baslik, parcalar) {
  const tablo = new Map();
  for (const [parca, alanlar] of Object.entries(parcalar))
    for (const alan of alanlar) tablo.set(alan, [...(tablo.get(alan) ?? []), parca]);

  let toplam = 0;
  console.log(baslik);
  for (const [alan, liste] of [...tablo].sort()) {
    toplam += liste.length;
    console.log(`  ${alan.padEnd(14)} ${String(liste.length).padStart(2)} yol  ${liste.join(", ")}`);
  }
  console.log(`  ${String(Object.keys(parcalar).length).padStart(2)} parca, ` +
    `${String(tablo.size).padStart(2)} alan, ${String(toplam).padStart(2)} yol\n`);
  return { parca: Object.keys(parcalar).length, alan: tablo.size, yol: toplam };
}

const once = ozet("Pano — ilk hali", PANO);

// Iki istek: (1) olcum birimi degistirilebilsin — birim, satirlari da rozeti de
// etkiler; (2) pano bir ozet grafigi ve bir esik uyarisi da gostersin.
const SONRA = ozet("Pano — iki istek sonrasi", {
  ...PANO,
  "rozet.birim": ["birim"],
  "tablo.satirlar": ["olcumler", "suzgec", "birim"],
  "grafik.noktalar": ["olcumler", "suzgec", "birim"],
  "uyari.serit": ["olcumler", "esikler", "suzgec"],
});

const yuzde = (y, e) => `%${Math.round((y / e - 1) * 100)}`;
console.log(`parca ${yuzde(SONRA.parca, once.parca)}, ` +
  `alan ${yuzde(SONRA.alan, once.alan)}, yol ${yuzde(SONRA.yol, once.yol)} artti`);
```

```
Pano — ilk hali
  esikler         1 yol  rozet.renk
  olcumler        4 yol  rozet.deger, rozet.renk, tablo.satirlar, tablo.sayac
  sonGuncelleme   1 yol  baslik.zaman
  suzgec          3 yol  tablo.satirlar, tablo.sayac, suzgec.secim
   6 parca,  4 alan,  9 yol

Pano — iki istek sonrasi
  birim           3 yol  tablo.satirlar, rozet.birim, grafik.noktalar
  esikler         2 yol  rozet.renk, uyari.serit
  olcumler        6 yol  rozet.deger, rozet.renk, tablo.satirlar, tablo.sayac, grafik.noktalar, uyari.serit
  sonGuncelleme   1 yol  baslik.zaman
  suzgec          5 yol  tablo.satirlar, tablo.sayac, suzgec.secim, grafik.noktalar, uyari.serit
   9 parca,  5 alan, 17 yol

parca %50, alan %25, yol %89 artti
```

Parça sayısı yarı yarıya arttı, elle yazılacak güncelleme yolu neredeyse ikiye katlandı.
Bunun nedeni, yeni parçaların var olan alanları da okumasıdır: grafik `olcumler`, `suzgec`
ve `birim` alanlarına bağlanınca bu üç alanı değiştiren her işleyiciye bir satır daha
eklenmesi gerekti.

Bağlantı sayısı en kötü durumda alan sayısı ile parça sayısının çarpımına yaklaşır. Bu
çarpım, tek bir kişinin akılda tutabileceği bir sayı olmaktan hızla çıkar; sayfaya yeni
bir parça ekleyen kişinin, o parçanın okuduğu her alanı kimin nerede değiştirdiğini
bilmesi gerekir.

## Durumun İki Kopyası

Elle güncellemede durum iki yerde bulunur: program değişkenlerinde ve ağacın kendisinde.
Ağaçtaki kopya eksiktir ve kayıplıdır.

Ağaç yalnızca dizgi saklar. Bir sayı öznitelik olarak yazıldığında tipini kaybeder; `-4.2`
ile `"-4.2"` arasındaki farkı geri getirmek okuyan tarafın işidir. Ağaç, ekranda
görünmeyen durumu hiç saklamaz: süzgeç panelindeki seçim bir sınıf adına indirgenmişse,
seçimin gerekçesi ya da önceki değeri kaybolmuştur.

Buradan bir kural çıkar: **ağaç bir depo değil, bir çıktıdır.** Ağaçtan okuyarak karar
veren kod, kendi ürettiği çıktıyı girdi olarak kullanır ve iki kopya arasındaki her
ayrışmada yanlış karar verir.

## Kaymanın Anatomisi

İki kopyanın ayrışmasına **kayma** denir. Aşağıdaki model kaymayı, durumdan hesaplanan
beklenen metinle ağaçtaki metni karşılaştıran bir denetimle yakalar.

```js
// kayma.mjs — elle guncellemede durum ile agacin ayrisma anini yakalar
// Kucuk bir agac modeli: her dugumun bir adi ve bir metni var.
const agac = {
  "tablo.sayac": { metin: "" },
  "tablo.satirlar": { metin: "" },
  "suzgec.secim": { metin: "" },
};
const yaz = (ad, metin) => { agac[ad].metin = metin; };

const durum = {
  olcumler: [
    { ad: "Sıcaklık", tur: "sicaklik", deger: -4.2 },
    { ad: "Bağıl nem", tur: "nem", deger: 72 },
    { ad: "Rüzgâr", tur: "ruzgar", deger: 11.4 },
    { ad: "Kar derinliği", tur: "kar", deger: 38 },
  ],
  suzgec: "tumu",
  esik: 50,
};

// Durumdan beklenen metinler. Denetim bunlari agactakiyle karsilastirir.
const gorunenler = (d) =>
  d.suzgec === "tumu" ? d.olcumler : d.olcumler.filter((o) => o.deger >= d.esik);
const beklenen = (d) => ({
  "tablo.sayac": `${gorunenler(d).length} / ${d.olcumler.length} ölçüm`,
  "tablo.satirlar": gorunenler(d).map((o) => o.ad).join(" | "),
  "suzgec.secim": d.suzgec,
});

// Ilk kurulum: her dugum elle yaziliyor.
for (const [ad, metin] of Object.entries(beklenen(durum))) yaz(ad, metin);

// Olay isleyicileri agaca dogrudan yaziyor. Ikisi de eksiksiz gorunuyor.
function suzgecDegisti(yeni) {
  durum.suzgec = yeni;
  yaz("suzgec.secim", yeni);
  yaz("tablo.satirlar", gorunenler(durum).map((o) => o.ad).join(" | "));
  yaz("tablo.sayac", `${gorunenler(durum).length} / ${durum.olcumler.length} ölçüm`);
}
function olcumEklendi(olcum) {
  durum.olcumler.push(olcum);
  yaz("tablo.satirlar", gorunenler(durum).map((o) => o.ad).join(" | "));
  // "tablo.sayac" burada yazilmiyor: ekleme yolunu yazan kisi sayaci gormedi.
}

function denetle(etiket) {
  const b = beklenen(durum);
  const kayanlar = Object.keys(b).filter((ad) => agac[ad].metin !== b[ad]);
  console.log(`${etiket}: ${kayanlar.length ? "kayma var" : "tutarli"}`);
  for (const ad of kayanlar)
    console.log(`  ${ad}\n    agac    : ${agac[ad].metin}\n    beklenen: ${b[ad]}`);
}

denetle("kurulum");
suzgecDegisti("esikUstu");
denetle("suzgec degisti");
olcumEklendi({ ad: "Basınç", tur: "basinc", deger: 964 });
denetle("olcum eklendi");
suzgecDegisti("esikUstu");
denetle("suzgec yeniden secildi");
```

```
kurulum: tutarli
suzgec degisti: tutarli
olcum eklendi: kayma var
  tablo.sayac
    agac    : 1 / 4 ölçüm
    beklenen: 2 / 5 ölçüm
suzgec yeniden secildi: tutarli
```

Çıktının üç yeri okunmalıdır.

Birincisi, hata **ekleme yolunun eksikliğinden** doğar, ekleme kodunun yanlışlığından
değil. `olcumEklendi` yazdığı satırı doğru yazar; yazmadığı satır sorundur. Bu tür bir
eksiklik derleyici, tip denetimi ya da birim testi tarafından yakalanmaz, çünkü ortada
çağrılmayan bir işlev yoktur.

İkincisi, kayma **kendini onarır**. Süzgeç yeniden seçildiğinde sayaç doğru değeri alır ve
tutarlılık geri gelir. Kendini onaran hata, yeniden üretilmesi en zor hata türüdür:
kullanıcının anlattığı sırayı birebir izlemeyen hiç kimse onu göremez.

Üçüncüsü, aynı metin **üç ayrı yerde** hesaplanmıştır — ilk kurulumda, süzgeç işleyicisinde
ve denetimde. Bu tekrar, kaymanın kaynağıdır. Metin tek bir yerde hesaplansaydı eksik
güncelleme olanaksız olurdu.

## Sıra, Kimlik ve Temizlik

Liste güncellemesi elle yazıldığında üç sorun daha eklenir.

**Sıra.** Listenin ortasına bir kayıt girdiğinde hangi düğümün nereye taşınacağını çağıran
kod hesaplar. Kolay yol, listeyi silip yeniden kurmaktır; bu, odaklanmış girdiyi kaybeder,
kaydırma konumunu sıfırlar ve süren geçişleri keser.

**Kimlik.** Bir düğüm hangi kayda karşılık geliyor sorusunun yanıtı ağaçta yazılı değildir.
Dizinle eşleştirilen düğümler, listenin başına ekleme yapıldığında bir kayarak yanlış
kayıtla eşleşir. Bu bağ elle kurulduğunda genellikle bir veri özniteliğine yazılır ve her
sıralamada güncellenmesi gerekir.

**Temizlik.** Kaldırılan her düğümün dinleyicisi kaldırılmalıdır; kaldırılmazsa Bellek
İncelemesi dersindeki kopmuş düğümler birikir. Sökme kodunun oluşturma kodunun tersini tam
olarak yapması gerekir ve bu simetriyi de hiçbir şey denetlemez.

## Çerçevenin Devraldığı İş

Buradaki sorunların ortak kaynağı tektir: **arayüz, nasıl değiştirileceğiyle
tanımlanmıştır.** Kodun her satırı bir geçişi anlatır; hiçbir satır sonucu anlatmaz.

**Arayüz çerçevesi (framework)**, bu tanımı tersine çeviren altyapıdır. Yazılan şey, verili
bir durumda arayüzün **ne olması gerektiğidir**; iki durum arasındaki farkı bulmak ve
ağaca uygulamak çerçevenin işidir. Çerçeve karşılığında dört sorumluluk devralır:

- durumdan görünüm tanımı üretmek,
- iki görünüm tanımı arasındaki farkı hesaplamak,
- farkı ağaca en az işlemle yazmak,
- kaldırılan parçaların dinleyicilerini ve kaynaklarını sökmek.

Bu kursta **çerçeve** sözcüğü yalnızca bu anlamda kullanılır; Ağ Modelleri ve Protokoller
kursundaki bağlantı katmanı çerçevesiyle ve gömülü içerik çerçevesiyle ilgisi yoktur.

Devir bedelsiz değildir. Fark hesabı çalışma zamanında yapılır ve maliyeti vardır; çerçeve
kendi kurallarını dayatır; hata ayıklarken araya bir katman girer. Bu bedelin ne zaman
ödendiği kursun son konusunda ölçülür. Kazanç ise bu derste sayılan şeydir: güncelleme
yolları ortadan kalkar, çünkü artık tek bir yol vardır.

## Özet

- Elle güncelleme her parça için ilk oluşturma, güncelleme ve sökme kodunu ayrı ayrı
  yazmayı gerektirir; üçünün aynı görünümü betimlediğini hiçbir şey denetlemez.
- Güncelleme kodunun büyüklüğü parça sayısıyla değil, durum alanı ile parça arasındaki
  bağlantı sayısıyla artar; bu sayı en kötü durumda alan ve parça sayısının çarpımıdır.
- Ağaç bir depo değil çıktıdır: dizgi dışında tip saklamaz, görünmeyen durumu hiç saklamaz.
- Kayma, aynı metnin birden çok yerde hesaplanmasından doğar; kendini onaran biçimi onu
  yeniden üretilmesi zor bir hataya dönüştürür.
- Liste güncellemesi elle yazıldığında sıra, kimlik ve temizlik sorunları eklenir.
- Arayüz çerçevesi, "nasıl değiştirileceği" tanımını "ne olması gerektiği" tanımıyla
  değiştirir ve fark hesabını üstlenir.

## Sonraki Adım

Çerçevenin devraldığı işin ilk maddesi, durumdan bir görünüm tanımı üretmektir. Bu, kulağa
geldiğinden daha katı bir gerekliliktir: tanımı üreten kodun aynı durum için her zaman aynı
sonucu vermesi, ağacın o anki hâline hiç bakmaması ve yazarken hiçbir şeyi değiştirmemesi
gerekir. Sonraki ders bu üretimi bir işlev olarak kurar, tek yönlü akışın ne anlama
geldiğini gösterir ve görünümün tamamını her seferinde yeniden üretmenin doğru ama pahalı
olduğunu ölçer.
