---
title: Kapsülleme
source: 'https://academia.sh/tr/kurslar/paradigmalar/kapsulleme'
course: 'Programlama Paradigmaları'
language: tr
updated: '2026-08-17T18:10:42+00:00'
license: 'CC BY-SA 4.0'
---

# Kapsülleme

Değişmezin sahibini belirleyen araç olarak kapsülleme: alanları açık ve kapalı iki sevkiyat sürümüne aynı işlem dizisinin uygulanması, bozuk duruma düşen nesne sayısının sayılması ve dönüş değeri kapsanmadığında korumanın nerede kırıldığı.

Önceki ders bölünmüş ücretlendirme akışının bir boşluk bıraktığını gösterdi: "ücret
hesaplanan her gönderi doğrulanmıştır" kuralı hiçbir yerde korunmuyor, yalnız
`ucretHesapla` yordamının içindeki çağrı sırasında yaşıyordu. Yordamlar veriyi argüman
olarak alır ve verinin geçmişi hakkında hiçbir güvence taşımaz.

**Kapsülleme** (encapsulation), bu boşluğu kapatan araçtır: veriyi ve onu değiştiren
işlemleri tek bir birimde toplar, veriye erişimi bu işlemlerle sınırlar. Programlama
Temelleri kursunda tanıtılan tanım budur. Bu derste tanım tekrarlanmaz; sorulan soru
farklıdır — kapsülleme ne zaman gerçekten çalışır, nerede kırılır ve bedeli nedir.

## Kapsülleme Neyi Kapatır

Yaygın bir yanlış okuma, kapsüllemenin "tüm alanları gizlemek" olduğudur. Ölçüt gizlilik
değil, **değişmez** (invariant). Bir alan, bir değişmezin denklemine giriyorsa dışarıdan
yazılamamalıdır; girmiyorsa gizlenmesi bir şey kazandırmaz.

Bu kursun kitaplığında bir `sevkiyat`, bir araca yüklenen gönderiler kümesidir ve üç
değişmez taşır:

1. Kayıtlı toplam ağırlık, yüklü gönderilerin ağırlık toplamına eşittir.
2. Toplam ağırlık, aracın kapasitesini aşmaz.
3. Mühürlenmiş bir sevkiyata gönderi eklenmemiştir.

`kapasiteGram` ikinci değişmezin bir tarafıdır; dışarıdan yazılabilirse değişmez tek
atamayla çiğnenir. `aracId` ise hiçbir denklemde geçmez; onu gizlemek yalnız erişimci
yazdırır. Kapsülleme kararı alan alan verilir, sınıf toptan kapatılarak değil.

Değişmezlerin denetimi tek bir yerde yazılır ki iki sürüme de aynı ölçüt uygulanabilsin.
Aynı dosyada, koşumların yinelenebilir olması için sabit tohumlu bir sayı üreteci de
bulunuyor.

```js
// denetci.mjs — uc degismezi tek yerde tanimlar; iki surume de ayni denetim uygulanir
export function ihlaller(d) {
  const bozuk = [];
  const toplam = d.gonderiler.reduce((t, g) => t + g.agirlikGram, 0);
  if (toplam !== d.toplamAgirlik) bozuk.push("toplam-tutarsiz");
  if (d.toplamAgirlik > d.kapasiteGram) bozuk.push("kapasite-asildi");
  if (d.muhurlu && d.gonderiler.length > d.muhurdekiSayi) bozuk.push("muhur-sonrasi-ekleme");
  return bozuk;
}

export function uretec(tohum) {
  let x = tohum;
  return () => (x = (x * 1103515245 + 12345) % 2147483648) / 2147483648;
}
```

## Alanları Açık Sürüm

İlk sürüm sevkiyatı düz bir nesne olarak tutar ve kuralları bir `ekle` yordamına yazar.
Kurallar burada **vardır**; eksik olan, onlardan geçmenin zorunlu olmasıdır.

```js
// sevkiyat-acik.mjs — alanlari disariya acik sevkiyat; kurallar ekle() icinde ama zorunlu degil
export function sevkiyatOlustur(aracId, kapasiteGram) {
  return { aracId, kapasiteGram, gonderiler: [], toplamAgirlik: 0, muhurlu: false, muhurdekiSayi: 0 };
}

export function ekle(sevkiyat, gonderi) {
  if (sevkiyat.muhurlu) return false;
  if (sevkiyat.toplamAgirlik + gonderi.agirlikGram > sevkiyat.kapasiteGram) return false;
  sevkiyat.gonderiler.push(gonderi);
  sevkiyat.toplamAgirlik += gonderi.agirlikGram;
  return true;
}

export function muhurle(sevkiyat) {
  sevkiyat.muhurlu = true;
  sevkiyat.muhurdekiSayi = sevkiyat.gonderiler.length;
}
```

Bu tasarımda `sevkiyat.gonderiler.push(g)` yazmak, `ekle(sevkiyat, g)` yazmaktan kısadır
ve aynı görünen sonucu üretir. Büyüyen bir kod tabanında bu kısa yol er geç kullanılır:
bir aktarma adımında, bir içe aktarma betiğinde, bir hata düzeltmesinde. Kısa yolun
sonucu, atlanan iki denetimdir.

## Alanları Kapalı Sürüm

İkinci sürümde değişmeze giren dört alan özeldir. Dışarıdan görünen tek yol iki yöntemdir.

```js
// sevkiyat-kapali.mjs — degismeze giren alanlar ozel; her degisiklik iki yontemden gecer
export class Sevkiyat {
  #kapasiteGram;
  #gonderiler = [];
  #toplamAgirlik = 0;
  #muhurlu = false;
  #muhurdekiSayi = 0;

  constructor(aracId, kapasiteGram) {
    if (kapasiteGram <= 0) throw new RangeError("kapasite pozitif olmali");
    this.aracId = aracId;
    this.#kapasiteGram = kapasiteGram;
  }

  ekle(gonderi) {
    if (this.#muhurlu) return false;
    if (this.#toplamAgirlik + gonderi.agirlikGram > this.#kapasiteGram) return false;
    this.#gonderiler.push({ ...gonderi });
    this.#toplamAgirlik += gonderi.agirlikGram;
    return true;
  }

  muhurle() {
    this.#muhurlu = true;
    this.#muhurdekiSayi = this.#gonderiler.length;
  }

  durum() {
    return {
      kapasiteGram: this.#kapasiteGram,
      gonderiler: this.#gonderiler.map((g) => ({ ...g })),
      toplamAgirlik: this.#toplamAgirlik,
      muhurlu: this.#muhurlu,
      muhurdekiSayi: this.#muhurdekiSayi,
    };
  }
}
```

`aracId` açık kaldı: hiçbir değişmezin parçası değil. `ekle` yönteminin içindeki
`{ ...gonderi }` bir **savunma kopyasıdır**; çağıranın elinde kalan nesne sonradan
değiştirilse bile sevkiyatın toplamı bozulmaz. `durum()` de aynı gerekçeyle kopya
döndürür — bu kararın bedeli dersin ilerleyen bölümünde ölçülecek.

## Aynı İşlem Dizisi, İki Sonuç

Ölçüm şu biçimde kurulur: sabit tohumlu bir üreteçle 200 sevkiyat için 20'şer işlemlik bir
dizi üretilir; aynı dizi iki sürüme de uygulanır. Dizideki işlemlerin bir bölümü kısa
yoldur — açık sürümde doğrudan alan yazımına, kapalı sürümde ise `ekle` çağrısına karşılık
gelir, çünkü orada başka yol yoktur.

```js
// kosum.mjs — ayni islem dizisi iki surume uygulanir, bozuk duruma dusen nesne sayilir
import { sevkiyatOlustur, ekle, muhurle } from "./sevkiyat-acik.mjs";
import { Sevkiyat } from "./sevkiyat-kapali.mjs";
import { ihlaller, uretec } from "./denetci.mjs";

const NESNE = 200;
const ISLEM = 20;

function islemler() {
  const rast = uretec(20250729);
  const dizi = [];
  for (let n = 0; n < NESNE; n += 1) {
    const tek = [];
    for (let i = 0; i < ISLEM; i += 1) {
      const g = { id: `${n}-${i}`, agirlikGram: 100 + Math.floor(rast() * 3900) };
      const r = rast();
      if (r < 0.10) tek.push({ tur: "muhurle" });
      else if (r < 0.45) tek.push({ tur: rast() < 0.5 ? "kisayol-tam" : "kisayol-yarim", g });
      else tek.push({ tur: "ekle", g });
    }
    dizi.push(tek);
  }
  return dizi;
}

const dizi = islemler();

let acikBozuk = 0;
const acikIhlal = new Map();
for (const tek of dizi) {
  const s = sevkiyatOlustur("34ABC01", 20000);
  for (const op of tek) {
    if (op.tur === "muhurle") muhurle(s);
    else if (op.tur === "ekle") ekle(s, op.g);
    else if (op.tur === "kisayol-tam") { s.gonderiler.push(op.g); s.toplamAgirlik += op.g.agirlikGram; }
    else s.gonderiler.push(op.g);
  }
  const bozuk = ihlaller(s);
  if (bozuk.length > 0) acikBozuk += 1;
  for (const ad of bozuk) acikIhlal.set(ad, (acikIhlal.get(ad) ?? 0) + 1);
}

let kapaliBozuk = 0;
let reddedilen = 0;
for (const tek of dizi) {
  const s = new Sevkiyat("34ABC01", 20000);
  for (const op of tek) {
    if (op.tur === "muhurle") s.muhurle();
    else if (s.ekle(op.g) === false) reddedilen += 1;
  }
  if (ihlaller(s.durum()).length > 0) kapaliBozuk += 1;
}

console.log(`nesne sayisi          = ${NESNE}`);
console.log(`acik surum bozuk      = ${acikBozuk}`);
console.log(`kapali surum bozuk    = ${kapaliBozuk}`);
console.log(`kapali surum reddetti = ${reddedilen}`);
for (const [ad, sayi] of [...acikIhlal].sort()) console.log(`  ihlal ${ad.padEnd(21)} = ${sayi}`);
```

```sh
node kosum.mjs
```

```
nesne sayisi          = 200
acik surum bozuk      = 198
kapali surum bozuk    = 0
kapali surum reddetti = 2428
  ihlal kapasite-asildi       = 43
  ihlal muhur-sonrasi-ekleme  = 137
  ihlal toplam-tutarsiz       = 195
```

İki yüz sevkiyatın 198'i açık sürümde en az bir değişmezi çiğnedi. Kapalı sürümde bozuk
nesne yok — ama asıl bilgi son satırda: kapalı sürüm 2428 işlemi **reddetti**. Aynı 2428
işlem açık sürümde sessizce kabul edilmişti.

Bu, kapsüllemenin ne yapıp ne yapmadığını tam olarak söyler. Kapsülleme talebi ortadan
kaldırmaz; sessiz bozulmayı **görünür bir redde** çevirir. Reddin çağırana nasıl
bildirileceği ayrı bir tasarım kararıdır: burada `false` dönüldü, sebebi taşıyan bir sonuç
nesnesi de dönülebilirdi. Ölçülebilir olan, bozulmanın 198'den 0'a inmesidir.

## Koruma Nerede Kırılır

Özel alan tek başına yetmez. Bir yöntem iç veriyi olduğu gibi döndürüyorsa, çağıran o
veriyi değiştirerek değişmezi dışarıdan bozabilir. Aşağıdaki sınıf alanları özel tutar ama
`manifesto()` iç diziyi doğrudan verir.

```js
// sizdiran-sevkiyat.mjs — alanlar ozel ama manifesto() ic diziyi oldugu gibi dondurur
export class SizdiranSevkiyat {
  #kapasiteGram; #gonderiler = []; #toplamAgirlik = 0;
  constructor(kapasiteGram) { this.#kapasiteGram = kapasiteGram; }
  ekle(gonderi) {
    if (this.#toplamAgirlik + gonderi.agirlikGram > this.#kapasiteGram) return false;
    this.#gonderiler.push({ ...gonderi });
    this.#toplamAgirlik += gonderi.agirlikGram;
    return true;
  }
  manifesto() { return this.#gonderiler; }
  kopyaManifesto() { return this.#gonderiler.map((g) => ({ ...g })); }
  durum() {
    return { kapasiteGram: this.#kapasiteGram, gonderiler: this.kopyaManifesto(),
             toplamAgirlik: this.#toplamAgirlik, muhurlu: false, muhurdekiSayi: 0 };
  }
}
```

Çağıran taraf, manifestoyu alıp aktarma parçasını listeye ekleyen sıradan bir adımdır.

```js
// sizinti.mjs — donus degeri kapsanmadiginda ozel alanin korumasi ne kadar dayanir
import { SizdiranSevkiyat } from "./sizdiran-sevkiyat.mjs";
import { ihlaller, uretec } from "./denetci.mjs";

function kosu(manifestoAl) {
  const rast = uretec(20250729);
  let bozuk = 0;
  for (let n = 0; n < 200; n += 1) {
    const s = new SizdiranSevkiyat(20000);
    for (let i = 0; i < 8; i += 1) {
      s.ekle({ id: `${n}-${i}`, agirlikGram: 100 + Math.floor(rast() * 3900) });
    }
    // aktarma adimi: manifestoyu alip aktarma parcasini listeye ekler
    manifestoAl(s).push({ id: `${n}-aktarma`, agirlikGram: 2500 });
    if (ihlaller(s.durum()).length > 0) bozuk += 1;
  }
  return bozuk;
}

console.log(`ic diziyi donduren manifesto  -> bozuk nesne = ${kosu((s) => s.manifesto())}`);
console.log(`kopya donduren manifesto      -> bozuk nesne = ${kosu((s) => s.kopyaManifesto())}`);
```

```sh
node sizinti.mjs
```

```
ic diziyi donduren manifesto  -> bozuk nesne = 200
kopya donduren manifesto      -> bozuk nesne = 0
```

İki yüz nesnenin tamamı bozuldu; tek fark, döndürülen değerin kopya olup olmamasıydı.
Kapsüllemenin sınırı erişim belirtecinde değil, **nesnenin dışına çıkan her referansta**
çizilir. Aynı gözlem yapıcıya giren referanslar için de geçerlidir; kapalı sürümün `ekle`
yönteminde kopya alınmasının nedeni buydu.

## Bedel

Kopya döndürmek bedava değildir. `durum()` çağrısı, gönderi sayısıyla orantılı iş yapar;
sıcak bir döngüde çağrılırsa maliyet ölçülebilir hâle gelir. Üç yaygın çıkış yolu vardır:
değiştirilemez öge kullanmak (kopya gereksizleşir), salt okunur bir görünüm döndürmek, ya
da listeyi hiç döndürmeyip sorulan soruyu nesneye sormak — "kaç gönderi var", "toplam
ağırlık nedir".

İkinci bedel, kapsüllemenin yanlış yere uygulanmasıdır. Hiçbir değişmez taşımayan bir veri
taşıyıcısında — bir adres kaydı, bir tarife satırı — her alan için erişimci yazmak, açık
alanla aynı korumayı sıfır kazançla pahalıya alır. Ölçüt değişmedi: kapatılan şey bir
kuralı koruyorsa kapsülleme, korumuyorsa tören.

## Özet

- Kapsüllemenin ölçütü gizlilik değil değişmezdir; bir alan bir değişmezin denklemine
  girmiyorsa gizlenmesi kazanç sağlamaz.
- Aynı 4000 işlemlik dizi altında alanları açık sürümde 200 sevkiyatın 198'i bozuk duruma
  düştü, kapalı sürümde hiçbiri düşmedi.
- Kapsülleme talebi yok etmez, sessiz bozulmayı görünür redde çevirir: kapalı sürüm 2428
  işlemi reddetti, açık sürüm aynı işlemleri sessizce kabul etmişti.
- İç veriyi olduğu gibi döndüren bir yöntem korumayı tümüyle kaldırır; iç diziyi döndüren
  sürümde 200 nesnenin 200'ü bozuldu, kopya döndürende hiçbiri bozulmadı.
- Kopyanın bedeli gönderi sayısıyla orantılıdır; alternatifleri değiştirilemez öge, salt
  okunur görünüm ve listeyi döndürmek yerine soruyu nesneye sormaktır.

## Sonraki Adım

Kapsülleme, verinin dışarıdan yazılmasını engelledi. Ama sevkiyatın dışarıya kaç ad
açtığı, bu adların ne kadar iç ayrıntı sızdırdığı ve bir ayrıntı değiştiğinde kaç çağrı
noktasının etkilendiği henüz sorulmadı. Sonraki ders bu soruyu ele alır: bir modülün dışa
açtığı ad sayısı ile istemcinin bilmek zorunda olduğu ayrıntı sayısı ölçülür, temsil
değiştirildiğinde iki tasarımda kaç dosyanın kırıldığı sayılır.
