---
title: 'Değer Nesneleri'
source: 'https://academia.sh/tr/kurslar/alan-odakli-tasarim/deger-nesneleri'
course: 'Alan Odaklı Tasarım'
language: tr
updated: '2026-08-17T18:10:41+00:00'
license: 'CC BY-SA 4.0'
---

# Değer Nesneleri

Değerle tanımlanan değişmez nesnelerin seçim ölçütü: gram cinsinden gelen on iki girdinin çıplak sayı modelinde kaç yanlış tutar ürettiğinin sayılması, eşitlik kuralının kaç satırda elle yazıldığının ölçülmesi, paylaşılan bir ölçünün yerinde düzeltilmesiyle kaç gönderinin bozulduğunun çıkarılması ve iki modelin aynı gruplamayı verdiğinin gösterilmesi.

Önceki dersin son bölümü kimlik gerektirmeyen kavramları saydı ama onları hâlâ çıplak sayı
olarak tuttu: ağırlık bir sayıydı, bölge bir katardı. O zaman kilogramla gramı aynı
parametreye geçirmek, ikisini toplamak ve karşılaştırmak kodun izin verdiği işlemler olur.

Kimliği olmayan, eşitliği taşıdığı değerle tanımlanan ve bir kez kurulduktan sonra
değişmeyen nesneye **değer nesnesi** (value object) denir. Tanım Nesneye Dayalı
Programlamaya Giriş kursunda verilmişti. Buradaki iş yine seçim ölçütüdür ve üç soruya
ayrılır: kimlik gerekiyor mu, eşitlik neye göre kurulacak, değişmezlik neyi ucuzlatıyor.

## Ölçüt

Önceki dersin iki sorusu bir kavram için de hayır yanıtı veriyorsa o kavram varlık nesnesi
değildir: iki tanesi arasında ayrım yoktur ve bir yaşam öyküsü taşımaz. Ağırlık böyledir. 2,4
kilogram ile bir başka 2,4 kilogram aynı şeydir; ağırlığın "düzeltilmesi" ağırlığın değişmesi
değil, gönderinin başka bir ağırlığa sahip olmasıdır.

İkinci soru eşitliğin neye göre kurulacağıdır ve yanıtı ölçünün birimini de içine alır. 2400
gram ile 2,4 kilogram eşittir; 2400 ile 2,4 eşit değildir. Aradaki fark, birimin nerede
tutulduğudur.

## Çıplak Sayı Modeli

Birinci sürümde birim yalnız parametre adında yazılıdır.

```sh
mkdir -p ciplak deger
```

```js
// ciplak/ucret.mjs — agirlik ciplak sayi, birim yalnizca parametre adinda
const KADEMELER = [[1, 3900], [5, 6400], [Infinity, 11800]];
const BOLGE_KATSAYISI = { "34": 1, "06": 1.35, "65": 1.8 };
const ASGARI_UCRET = 3990;

export function netUcret(agirlikKg, bolge, oran) {
  const taban = KADEMELER.find(([ust]) => agirlikKg <= ust)[1];
  const ham = taban * (BOLGE_KATSAYISI[bolge] ?? 1.8);
  return Math.max(Math.round(ham * (1 - oran)), ASGARI_UCRET);
}
```

```js
// ciplak/mutabakat.mjs — esitlik kurali her cagri yerinde yeniden yazilir
export const tartimUyuyorMu = (beyanKg, tartimKg) => Math.abs(beyanKg - tartimKg) < 0.001;
export const ayniKademeMi = (aKg, bKg) => Math.abs(aKg - bKg) < 0.001;
export const agirlikTopla = (aKg, bKg) => aKg + bKg;
```

```js
// ciplak/rapor.mjs — agirliga gore gruplama; esitlik kurali burada da yazili
export function agirlikGruplari(agirliklarKg) {
  const gruplar = [];
  for (const a of agirliklarKg) {
    const grup = gruplar.find((g) => Math.abs(g[0] - a) < 0.001);
    if (grup) grup.push(a);
    else gruplar.push([a]);
  }
  return gruplar;
}
```

Kayan noktalı sayıların karşılaştırılması bir hoşgörü payı gerektirir ve o pay üç dosyada üç
kez yazılmış durumda. Payın büyüklüğü bir alan kararıdır — kaç grama kadar iki ağırlık aynı
sayılır — ama kodda alan kararı gibi görünmüyor.

## Değer Nesnesi Modeli

İkinci sürümde ağırlık kendi tipini alır. Birim yapıcıda çözülür, iç gösterim gram olur,
nesne dondurulur.

```js
// deger/agirlik.mjs — degistirilemez deger nesnesi: birim yapinin icinde
const GRAM = { kg: 1000, g: 1 };

export class Agirlik {
  constructor(miktar, birim) {
    if (!(birim in GRAM)) throw new RangeError(`bilinmeyen agirlik birimi: ${birim}`);
    this.gram = Math.round(miktar * GRAM[birim]);
    Object.freeze(this);
  }
  static kg(miktar) { return new Agirlik(miktar, "kg"); }
  static g(miktar) { return new Agirlik(miktar, "g"); }
  kilogram() { return this.gram / 1000; }
  esitMi(o) { return o instanceof Agirlik && o.gram === this.gram; }
  arti(o) { return new Agirlik(this.gram + o.gram, "g"); }
}
```

Hoşgörü payı ortadan kalktı çünkü iç gösterim tam sayıdır: yuvarlama bir kez yapıcıda
yapılıyor, karşılaştırma tam sayı eşitliği oluyor. `arti` yeni bir nesne döndürüyor, çağıranı
değiştirmiyor.

```js
// deger/ucret.mjs — agirlik yerine Agirlik bekler, ciplak sayiyi reddeder
import { Agirlik } from "./agirlik.mjs";

const KADEMELER = [[1, 3900], [5, 6400], [Infinity, 11800]];
const BOLGE_KATSAYISI = { "34": 1, "06": 1.35, "65": 1.8 };
const ASGARI_UCRET = 3990;

export function netUcret(agirlik, bolge, oran) {
  if (!(agirlik instanceof Agirlik)) throw new TypeError("agirlik bir Agirlik olmali");
  const taban = KADEMELER.find(([ust]) => agirlik.kilogram() <= ust)[1];
  const ham = taban * (BOLGE_KATSAYISI[bolge] ?? 1.8);
  return Math.max(Math.round(ham * (1 - oran)), ASGARI_UCRET);
}
```

```js
// deger/mutabakat.mjs — esitlik kurali deger nesnesinin icinde, cagri yeri onu cagirir
export const tartimUyuyorMu = (beyan, tartim) => beyan.esitMi(tartim);
export const ayniKademeMi = (a, b) => a.esitMi(b);
export const agirlikTopla = (a, b) => a.arti(b);
```

```js
// deger/rapor.mjs — ayni gruplama; esitlik deger nesnesinin kendi yontemi
export function agirlikGruplari(agirliklar) {
  const gruplar = [];
  for (const a of agirliklar) {
    const grup = gruplar.find((g) => g[0].esitMi(a));
    if (grup) grup.push(a);
    else gruplar.push([a]);
  }
  return gruplar;
}
```

## Ölçüm

Üç ölçü alınır. Yirmi dört girdinin yarısı gram cinsinden gelir; kaç tanesinde sessizce
yanlış tutar üretildiği sayılır. Eşitlik kuralının kaç satırda yazıldığı taranır. Beş gönderi
aynı ölçüyü paylaşırken biri düzeltildiğinde kaç gönderinin bozulduğu ölçülür.

```js
// deger-olc.mjs — birim karismasi, esitlik kuralinin yeri ve paylasilan ornegin bozulmasi
import { readFileSync } from "node:fs";
import { netUcret as ciplakUcret } from "./ciplak/ucret.mjs";
import { netUcret as degerUcret } from "./deger/ucret.mjs";
import { Agirlik } from "./deger/agirlik.mjs";
import { agirlikGruplari as ciplakGrup } from "./ciplak/rapor.mjs";
import { agirlikGruplari as degerGrup } from "./deger/rapor.mjs";

const BOLGELER = ["34", "06", "65"];
const GIRDILER = [];
for (let i = 0; i < 24; i += 1) {
  const kg = [0.4, 0.9, 2.4, 3.1, 7, 12][i % 6];
  GIRDILER.push({ kaynak: i % 2 === 0 ? "kg" : "g", kg, bolge: BOLGELER[i % 3] });
}
// dogru tutar: kural, gercek kilogram degerine uygulanmis hali
const dogruUcret = (g) => ciplakUcret(g.kg, g.bolge, 0);

let ciplakYanlis = 0;
for (const g of GIRDILER) {
  const sayi = g.kaynak === "kg" ? g.kg : g.kg * 1000;
  if (ciplakUcret(sayi, g.bolge, 0) !== dogruUcret(g)) ciplakYanlis += 1;
}
let degerYanlis = 0;
let degerHata = 0;
for (const g of GIRDILER) {
  const sayi = g.kaynak === "kg" ? g.kg : g.kg * 1000;
  if (degerUcret(new Agirlik(sayi, g.kaynak), g.bolge, 0) !== dogruUcret(g)) degerYanlis += 1;
  try { degerUcret(sayi, g.bolge, 0); } catch { degerHata += 1; }
}
console.log(`24 girdinin 12'si gram cinsinden geliyor`);
console.log(`  ciplak: sessizce yanlis ucret ${ciplakYanlis}/24, firlatilan hata 0/24`);
console.log(`  deger : sessizce yanlis ucret ${degerYanlis}/24, ` +
  `ciplak sayi gecirildiginde hata ${degerHata}/24`);

const KURAL = /Math\.abs\(|\.gram ===/;
for (const [ad, dosyalar] of Object.entries({
  ciplak: ["ciplak/ucret.mjs", "ciplak/mutabakat.mjs", "ciplak/rapor.mjs"],
  deger: ["deger/ucret.mjs", "deger/mutabakat.mjs", "deger/rapor.mjs", "deger/agirlik.mjs"],
})) {
  const satir = dosyalar.flatMap((d) => readFileSync(d, "utf8").split("\n"));
  console.log(`${ad}: esitlik kurali yazan satir ${satir.filter((s) => KURAL.test(s)).length}`);
}

const OLCUM = [0.4, 2.4, 0.4, 7, 2.4, 12, 7, 0.9];
const cGrup = ciplakGrup(OLCUM).map((g) => g.length).join(",");
const dGrup = degerGrup(OLCUM.map((k) => Agirlik.kg(k))).map((g) => g.length).join(",");
console.log(`8 olcumun gruplanmasi -> ciplak [${cGrup}], deger [${dGrup}], ` +
  `ayni mi ${cGrup === dGrup}`);

const paylasilan = { miktarKg: 2.4 };
const gonderiler = [0, 1, 2, 3, 4].map(() => ({ agirlik: paylasilan }));
paylasilan.miktarKg = 3.1;
const bozulan = gonderiler.slice(1).filter((g) => g.agirlik.miktarKg !== 2.4).length;

const ilkAgirlik = Agirlik.kg(2.4);
const gonderilerD = [0, 1, 2, 3, 4].map(() => ({ agirlik: ilkAgirlik }));
gonderilerD[0].agirlik = Agirlik.kg(3.1);
const bozulanD = gonderilerD.slice(1).filter((g) => !g.agirlik.esitMi(Agirlik.kg(2.4))).length;
let yerindeHata = 0;
try { ilkAgirlik.gram = 3100; } catch { yerindeHata = 1; }

console.log(`5 gonderi ayni agirlik ornegini paylasiyor, biri duzeltiliyor`);
console.log(`  degistirilebilir kayit: bozulan gonderi ${bozulan}/4`);
console.log(`  deger nesnesi         : bozulan gonderi ${bozulanD}/4, ` +
  `yerinde degistirme denemesi hata ${yerindeHata}`);
```

```sh
node deger-olc.mjs
```

```
24 girdinin 12'si gram cinsinden geliyor
  ciplak: sessizce yanlis ucret 8/24, firlatilan hata 0/24
  deger : sessizce yanlis ucret 0/24, ciplak sayi gecirildiginde hata 24/24
ciplak: esitlik kurali yazan satir 3
deger: esitlik kurali yazan satir 1
8 olcumun gruplanmasi -> ciplak [2,2,2,1,1], deger [2,2,2,1,1], ayni mi true
5 gonderi ayni agirlik ornegini paylasiyor, biri duzeltiliyor
  degistirilebilir kayit: bozulan gonderi 4/4
  deger nesnesi         : bozulan gonderi 0/4, yerinde degistirme denemesi hata 1
```

Birinci ölçü birim sorusunu yanıtlıyor. Gram cinsinden gelen on iki girdinin sekizinde çıplak
sayı modeli yanlış tutar üretiyor ve bunu sessizce yapıyor: hata sayısı sıfırdır, çağıran
taraf hiçbir uyarı almaz. Kalan dört girdide tutar tesadüfen doğru çıkıyor, çünkü 7 ve 12
kilogramlık gönderiler gram olarak da en üst kademeye düşüyor; tesadüf bir güvence değildir.
Değer nesnesi modelinde yanlış tutar sayısı sıfır. Aynı modelde çıplak bir sayı geçirildiğinde
yirmi dört çağrının yirmi dördü hata fırlatıyor — sessiz yanlış, gürültülü hataya dönüşmüş
durumda.

İkinci ölçü eşitlik sorusunu yanıtlıyor. Hoşgörü payı çıplak modelde üç satırda yazılı; değer
nesnesi modelinde bir satırda, `esitMi` yönteminin içinde. Payın alan kararı olduğu ikinci
durumda görünür hâle geliyor: değişmesi gerekirse dokunulacak yer birdir.

Üçüncü ölçü değişmezliğin ne ucuzlattığını gösteriyor. Değiştirilebilir kayıt beş gönderi
tarafından paylaşıldığında bir düzeltme dördünü bozuyor. Değer nesnesi modelinde aynı
düzeltme yeni bir nesne üretiyor, dolayısıyla bozulan gönderi sayısı sıfır. Dondurulmuş
nesnede yerinde değiştirme denemesi hata veriyor. Kazanç bir savunma kopyası yazmak
zorunda kalmamaktır: paylaşım bedelsiz olur.

Gruplama satırı yine hizanın doğruluktan ayrı olduğunu söylüyor: sekiz ölçüm iki modelde de
aynı beş gruba dağılıyor.

## Değer Nesnesi Neyi Taşıyabilir

Değer nesnesinin taşıdığı asıl kazanç kendi kurallarını taşıyabilmesidir. Ağırlık negatif
olamaz, bölge kodu iki basamaklıdır, indirim oranı sıfırla bir arasındadır. Bu kurallar
çıplak sayı modelinde çağrı yerlerine dağılır; değer nesnesinde yapıcıya girer ve bir kez
yazılır. Bunun sonucu, kuralı bozan bir değerin **hiç var olamamasıdır** — sonraki dersin
ölçtüğü şey tam olarak bu tür ulaşılabilirliktir.

Kitaplıkta aynı işlemi gören ikinci aday para tutarıdır: kuruş ile lira karışması ağırlıkla
aynı hatayı üretir ve çözümü de aynıdır — tutarı kuruş cinsinden tam sayı olarak tutan,
eşitliği ve toplamayı kendi üzerinde taşıyan bir tip.

## Ne Zaman Karşılığını Vermez

Bedel iki kalemdir: her kavram için bir tip ve her erişim için bir yöntem çağrısı. Ölçüm
dosyalarında bu bedel görünüyor — değer nesnesi sürümü dört dosya tutuyor, çıplak sürüm üç.

Kavramın tek bir yerde okunup bir daha kullanılmadığı durumlarda bu bedel karşılıksız kalır.
Bir sınırdan geçen ham kayıtta ağırlık bir sayıdır; onu tipe çevirmenin kazancı ancak o değer
kitaplığın içinde dolaşmaya başladığında doğar. Aynı biçimde birimi tek olan ve hiç
karşılaştırılmayan bir ölçü — kütüğe yazılan bir sıra numarası gibi — tip açmayı hak etmez.

## Özet

- Değer nesnesi kimliği olmayan, eşitliği taşıdığı değerle kurulan ve kurulduktan sonra
  değişmeyen nesnedir; ölçütü üç sorudur: kimlik, eşitlik, değişmezlik.
- Gram cinsinden gelen on iki girdinin 8'inde çıplak sayı modeli sessizce yanlış tutar
  üretti; değer nesnesi modelinde yanlış tutar 0, çıplak sayı geçirildiğinde hata 24/24.
- Eşitliğin hoşgörü payı çıplak modelde 3 satırda, değer nesnesi modelinde 1 satırda yazılı;
  alan kararı ikinci durumda tek noktada durur.
- Beş gönderi aynı ölçüyü paylaşırken bir düzeltme değiştirilebilir kayıtta 4 gönderiyi
  bozdu, değer nesnesinde 0; dondurulmuş nesnede yerinde değiştirme hata verdi.
- Sekiz ölçüm iki modelde de aynı gruplara dağıldı: kazanç doğrulukta değil, yanlışın
  yazılamaz hâle gelmesindedir.
- Bedel her kavram için bir tip ve her erişim için bir çağrıdır; tek yerde okunup bırakılan
  ham değerlerde bu bedel karşılıksızdır.

## Sonraki Adım

Buraya kadar gönderi tek başına ele alındı. Alanda tek başına değildir: indirimleri,
sözleşmesi ve beyan kalemleri vardır ve aralarında bir kural durur — indirim oranlarının
toplamı tavanı aşamaz. Bu kural tek bir nesneye ait değildir, gönderi ile indirimlerinin
bütününe aittir. Sonraki ders o bütünün sınırını çizer: kuralı bozan bir durumun kodla kaç
ayrı çağrı yolundan üretilebildiğini sayar, sınırı dışa açan ve yalnız kökten geçiren iki
modeli karşılaştırır ve bir işlemde kaç ayrı bütünün yazıldığını ölçer.
