---
title: 'Nesne Sorumluluğu'
source: 'https://academia.sh/tr/kurslar/paradigmalar/nesne-sorumlulugu'
course: 'Programlama Paradigmaları'
language: tr
updated: '2026-08-17T18:10:42+00:00'
license: 'CC BY-SA 4.0'
---

# Nesne Sorumluluğu

Anemik veri taşıyıcısı ile davranışı veriyle tutan düzenin karşılaştırılması: aynı iki kuralın kaç dosyada yazıldığının sayılması, kural değiştirildiğinde arama tabanlı bir güncellemenin bir yeri atlaması ve 40 gönderinin 20'sinde ortaya çıkan tutarsızlığın ölçülmesi.

Önceki ders iki nesnenin birbirine nasıl bağlanacağını ölçtü. Bu dersin sorusu bir nesnenin
kendi içinde ne taşıyacağıdır. Sevkiyat sınıfı hem gönderileri hem kapasite kuralını
tutuyordu; kural veriyle aynı yerdeydi. Yaygın bir başka düzen bunu ayırır: veri yalnız alan
taşır, kurallar ayrı işlemci birimlerine dağılır.

Alan taşıyan ama hiçbir kural içermeyen nesnelere **anemik** (anemic) denir. Bu ders iki
düzeni aynı kurallar üzerinde karşılaştırır: bir kuralın kaç yerde yazıldığını sayar ve kural
değiştiğinde kaç sonucun birbirinden ayrıldığını ölçer.

## Anemik Düzen

Gönderi üç alan taşır ve başka hiçbir şey bilmez.

```js
// anemik/gonderi.mjs — yalnizca alan tasiyan veri nesnesi; hicbir kural burada degil
export const gonderi = (agirlikGram, hacimCm3, sozlesmeli = false) =>
  ({ agirlikGram, hacimCm3, sozlesmeli });
```

Kitaplıkta iki kural vardır. Birincisi **ücretli ağırlık**: ücrete esas alınan değer, gerçek
ağırlık ile hacimden türetilen ağırlığın büyüğüdür. İkincisi **sözleşmeli indirimi**:
sözleşmeli müşteride tutar 0,9 katsayısıyla çarpılır. Üç istemci de bu kuralları kendi
içinde uygular.

```js
// anemik/ucretlendirme.mjs — ucretli agirlik ve indirim kurallarini kendisi uygular
export const ucret = (g) => {
  const agirlik = Math.max(g.agirlikGram, g.hacimCm3 / 3);
  const oran = g.sozlesmeli ? 0.9 : 1;
  return { agirlik, tutar: Math.round(agirlik * 1.2 * oran) };
};
```

```js
// anemik/teklif.mjs — ayni iki kurali yeniden yazar
export const tahmin = (g) => {
  const agirlik = Math.max(g.agirlikGram, g.hacimCm3 / 3);
  const oran = g.sozlesmeli ? 0.9 : 1;
  return { agirlik, tutar: Math.round(agirlik * 1.5 * oran) };
};
```

Üçüncü istemci aynı kuralı yazar, ama böleni yerel bir sabite almıştır. Bu, tek başına
kusur sayılmayan sıradan bir yazım tercihidir; dersin ilerleyen bölümünde belirleyici hâle
gelecektir.

```js
// anemik/plan.mjs — ayni kural, boleni yerel bir sabite alinmis bicimde
const HACIM_BOLENI = 3;

export const yuk = (g) => {
  const agirlik = Math.max(g.agirlikGram, g.hacimCm3 / HACIM_BOLENI);
  return { agirlik, dolulukPayi: Math.round((agirlik / 20000) * 100) };
};
```

## Davranışı Veriyle Tutan Düzen

İkinci düzende iki kural gönderinin kendisine taşınır. Alanlar özeldir; dışarıya kural
sonuçları açılır.

```js
// sorumlu/gonderi.mjs — veri ile onu yorumlayan kural ayni birimde
export class Gonderi {
  #agirlikGram; #hacimCm3; #sozlesmeli;

  constructor(agirlikGram, hacimCm3, sozlesmeli = false) {
    this.#agirlikGram = agirlikGram;
    this.#hacimCm3 = hacimCm3;
    this.#sozlesmeli = sozlesmeli;
  }

  ucretliAgirlik() { return Math.max(this.#agirlikGram, this.#hacimCm3 / 3); }

  indirimOrani() { return this.#sozlesmeli ? 0.9 : 1; }
}
```

Aynı üç istemci artık hesaplamaz, sorar.

```js
// sorumlu/ucretlendirme.mjs — kurali sormaz, gonderiye sorar
export const ucret = (g) => {
  const agirlik = g.ucretliAgirlik();
  return { agirlik, tutar: Math.round(agirlik * 1.2 * g.indirimOrani()) };
};
```

```js
// sorumlu/teklif.mjs — ayni iki soruyu ayni yere sorar
export const tahmin = (g) => {
  const agirlik = g.ucretliAgirlik();
  return { agirlik, tutar: Math.round(agirlik * 1.5 * g.indirimOrani()) };
};
```

```js
// sorumlu/plan.mjs — yalnizca agirligi sorar
export const yuk = (g) => {
  const agirlik = g.ucretliAgirlik();
  return { agirlik, dolulukPayi: Math.round((agirlik / 20000) * 100) };
};
```

## Kural Kaç Yerde Yazılı

Ölçüm iki kuralı arar ve kaç dosyada göründüklerini sayar; aynı taramada istemcilerin
gönderinin alanlarına kaç kez doğrudan eriştiği de sayılır.

```js
// olcum.mjs — iki kuralin kac dosyada tekrarlandigini ve alanlara kac kez dogrudan erisildigini sayar
import { readdirSync, readFileSync } from "node:fs";

const KURAL = [
  ["ucretli agirlik", /Math\.max\([^)]*hacimCm3/],
  ["sozlesmeli indirimi", /sozlesmeli \? 0\.9/],
];
const ALAN = /\bg\.(agirlikGram|hacimCm3|sozlesmeli)\b/g;

for (const dizin of ["anemik", "sorumlu"]) {
  const dosyalar = readdirSync(dizin).sort().map((d) => [d, readFileSync(`${dizin}/${d}`, "utf8")]);
  console.log(dizin);
  for (const [ad, kalip] of KURAL) {
    const yerler = dosyalar.filter(([, k]) => kalip.test(k)).map(([d]) => d);
    console.log(`  ${ad.padEnd(20)} yazildigi dosya = ${yerler.length}  ${yerler.join(", ")}`);
  }
  const erisim = dosyalar.reduce((t, [, k]) => t + (k.match(ALAN) ?? []).length, 0);
  console.log(`  ${"alana dogrudan erisim".padEnd(20)} = ${erisim}`);
}
```

```sh
node olcum.mjs
```

```
anemik
  ucretli agirlik      yazildigi dosya = 3  plan.mjs, teklif.mjs, ucretlendirme.mjs
  sozlesmeli indirimi  yazildigi dosya = 2  teklif.mjs, ucretlendirme.mjs
  alana dogrudan erisim = 8
sorumlu
  ucretli agirlik      yazildigi dosya = 1  gonderi.mjs
  sozlesmeli indirimi  yazildigi dosya = 1  gonderi.mjs
  alana dogrudan erisim = 0
```

Ücretli ağırlık kuralı anemik düzende üç dosyada, davranışı veriyle tutan düzende bir
dosyada yazılı. Sözleşmeli indirimi ikiye karşı bir. Son satır bunun nedenini gösteriyor:
anemik düzende istemciler gönderinin alanlarına sekiz kez doğrudan erişiyor, öteki düzende
hiç erişmiyor. Alanı okuyan taraf, o alanın nasıl yorumlanacağını da bilmek zorundadır;
kuralın çoğalması bu bilme zorunluluğunun sonucudur.

## Kural Değiştiğinde

İki düzen de aynı sonucu üretir. Aşağıdaki denetleyici 40 gönderilik bir ızgarada üç
istemciyi çalıştırır ve üçünün bulduğu ücretli ağırlığın aynı olup olmadığını sayar.

```js
// tutarlilik.mjs — uc istemcinin ayni gonderi icin ayni ucretli agirligi bulup bulmadigini sayar
import { gonderi } from "./anemik/gonderi.mjs";
import { ucret as aUcret } from "./anemik/ucretlendirme.mjs";
import { tahmin as aTahmin } from "./anemik/teklif.mjs";
import { yuk as aYuk } from "./anemik/plan.mjs";
import { Gonderi } from "./sorumlu/gonderi.mjs";
import { ucret as sUcret } from "./sorumlu/ucretlendirme.mjs";
import { tahmin as sTahmin } from "./sorumlu/teklif.mjs";
import { yuk as sYuk } from "./sorumlu/plan.mjs";

const izgara = [];
for (const agirlik of [400, 900, 2500, 6000, 11000])
  for (const hacim of [600, 4500, 18000, 45000])
    for (const sozlesmeli of [false, true])
      izgara.push([agirlik, hacim, sozlesmeli]);

const ayrilan = (islemler, kur) => izgara.filter((satir) => {
  const agirliklar = islemler.map((f) => f(kur(satir)).agirlik);
  return new Set(agirliklar).size > 1;
}).length;

console.log(`gonderi sayisi     = ${izgara.length}`);
console.log(`anemik  ayrilan    = ${ayrilan([aUcret, aTahmin, aYuk], (s) => gonderi(...s))}`);
console.log(`sorumlu ayrilan    = ${ayrilan([sUcret, sTahmin, sYuk], (s) => new Gonderi(...s))}`);
```

```sh
node tutarlilik.mjs
```

```
gonderi sayisi     = 40
anemik  ayrilan    = 0
sorumlu ayrilan    = 0
```

Şimdi kural değişir: hacimden ağırlığa çeviren bölen 3'ten 4'e iner. Değişikliği yapan
taraf kuralı arar ve bulduğu her yeri günceller. Aşağıdaki blok bunu yapar; `sed -i`
seçeneği GNU ve BSD sürümlerinde farklı davrandığı için geçici dosya üzerinden yazılmıştır.

```sh
for f in $(grep -rl 'hacimCm3 / 3' anemik sorumlu | sort); do
  sed 's|hacimCm3 / 3|hacimCm3 / 4|' "$f" > "$f.yeni" && mv "$f.yeni" "$f"
  echo "guncellendi: $f"
done
node tutarlilik.mjs
```

```
guncellendi: anemik/teklif.mjs
guncellendi: anemik/ucretlendirme.mjs
guncellendi: sorumlu/gonderi.mjs
gonderi sayisi     = 40
anemik  ayrilan    = 20
sorumlu ayrilan    = 0
```

Arama anemik düzendeki üç yerin ikisini buldu. Üçüncüsü — böleni yerel bir sabite almış
olan `plan.mjs` — aramaya takılmadı ve eski kuralla kaldı. Sonuç, 40 gönderinin 20'sinde
ücretlendirmenin bir ağırlık, yükleme planının başka bir ağırlık kullanmasıdır. Aynı
gönderi iki ayrı ağırlıkla anılmaya başladı ve hiçbir yerde hata çıkmadı.

Davranışı veriyle tutan düzende aynı arama tek yeri buldu, çünkü kural tek yerdeydi.
Ayrılan sonuç sayısı sıfır kaldı.

Buradaki asıl gözlem, aramanın bir yeri kaçırmış olması değildir; her arama er geç bir yeri
kaçırır. Asıl gözlem, anemik düzende **kaçırmanın mümkün olmasıdır**. Kural bir yerdeyse
kaçırılacak ikinci bir yer yoktur.

## Sorma, Söyle

Ölçümdeki sekiz doğrudan erişim, kuralın çoğalmasının sebebiydi. Bunu tersine çeviren ilkeye
**sorma, söyle** (tell, don't ask) denir: bir nesneden verisini alıp üzerinde karar vermek
yerine, kararı nesneye söyletmek. Anemik istemci `g.hacimCm3` alanını alıp bölüyordu;
sorumlu istemci `g.ucretliAgirlik()` diyerek sonucu istiyordu.

İlkenin sınırı da ölçümde görünür. Bir gönderiye sorulabilecek şey, gönderinin kendi
verisiyle yanıtlayabildiği şeydir. Ücretli ağırlık ve indirim oranı böyledir. Bir tarifeye
göre tutar hesaplamak değildir: bu, gönderinin bilmediği bir ikinci nesneyi gerektirir.
Gönderiye tarifeyi de taşıtmak, sorumluluğu toplamak değil şişirmek olur — kitaplığın her
kuralı tek bir sınıfa yığılır. Ölçüt sabittir: bir kural, yalnız kendi verisiyle
yanıtlanabiliyorsa o verinin yanına konur.

## Alan Taşıyıcısının Yerinde Olduğu Durum

Anemik düzen her yerde kusur değildir. Bir sınırı geçen kayıtlar — ağdan gelen bir gövde,
bir veritabanı satırından okunan alanlar, dosyaya yazılan bir çıktı — davranış taşımaz ve
taşımamalıdır; işleri biçimi korumaktır, kural yorumlamak değil. Kusur, bu kayıtların
kuralları olan alan nesnelerinin **yerine** geçmesidir: sınırdan gelen kayıt olduğu gibi
sistemin içine yayılırsa, onu yorumlayan her nokta kuralı yeniden yazmak zorunda kalır ve
ölçülen üçe karşı bir tablosu ortaya çıkar.

Ayrım pratikte tek soruyla verilir: bu nesnenin taşıdığı alanlar bir kural tarafından
yorumlanıyor mu? Yorumlanıyorsa kural onunla birlikte durmalıdır. Yorumlanmıyorsa alan
taşıyıcısı yerindedir.

## Özet

- Anemik nesne yalnız alan taşır, kuralları ayrı işlemcilere bırakır; kuralın veriyle
  birlikte tutulması bunun karşıtıdır.
- Aynı iki kural anemik düzende üç ve iki dosyada yazılıydı; davranışı veriyle tutan düzende
  ikisi de tek dosyadaydı.
- Kuralın çoğalmasının kaynağı ölçüldü: anemik istemciler gönderinin alanlarına sekiz kez
  doğrudan erişiyordu, öteki düzende sıfır.
- Bölen 3'ten 4'e indirildiğinde arama anemik düzendeki üç yerin ikisini buldu; farklı
  yazılmış üçüncüsü eski kuralla kaldı ve 40 gönderinin 20'sinde ücretlendirme ile yükleme
  planı ayrı ağırlık kullandı, hiçbir hata çıkmadı.
- Aynı değişiklik davranışı veriyle tutan düzende tek dosyaya dokundu ve 0 ayrılma üretti.
- Kural nesneye ancak nesnenin kendi verisiyle yanıtlanabiliyorsa taşınır; sınırı geçen
  kayıtların alan taşıyıcısı olarak kalması yerindedir.

## Sonraki Adım

Bu konu boyunca birim **nesne**ydi: durum ve onu koruyan davranış bir arada tutuldu.
Kapsülleme durumu dışarıdan yazılmaya kapattı, soyutlama temsili gizledi, kalıtım ve bileşim
davranışın nasıl paylaşılacağını belirledi, bu ders de kuralı verinin yanına yerleştirdi.
Hepsinin ortak varsayımı, durumun bir sahibi olduğu ve o sahibin durumu zaman içinde
değiştirdiğidir.

Bir başka model durumu birimden tamamen çıkarır. Veri değişmez; davranış, girdiden çıktıya
bir eşlemedir; yan etki programın sınırına itilir. Sonraki konu bu modeli kurar ve saf
fonksiyonlarla başlar: aynı girdiye her zaman aynı çıktıyı veren, dışarıda hiçbir iz
bırakmayan bir fonksiyonun ne kazandırdığı ve neyin bedelini artırdığı ölçülür.
