---
title: 'Demeter Yasası'
source: 'https://academia.sh/tr/kurslar/tasarim-ilkeleri/demeter-yasasi'
course: 'Tasarım İlkeleri'
language: tr
updated: '2026-08-17T18:10:40+00:00'
license: 'CC BY-SA 4.0'
---

# Demeter Yasası

Zincirleme erişimin sınırlanması: erişim zinciri derinliğini sayan bir taramanın yazılması, aynı beş çıktıyı üreten iki tasarımda bulgu sayısının karşılaştırılması ve adres kaydı yeniden yapılandırıldığında düzenlenen dosya sayısının ölçülmesi.

Uyum ölçüsü bir modülün kendi adlarına dokunmasına baktı. Modüller birbirine nesne
geçirmeye başladığında yeni bir soru çıkar: bir işlev, aldığı nesnenin içinden aldığı ikinci
bir nesnenin üçüncü bir alanına eriştiğinde kaç ayrı yapıyı bilmek zorunda kalır? Bu bağımlılık
ne ithal grafiğinde ne de uyum ölçüsünde görünür, çünkü ortada yeni bir ithal ya da yeni bir
modül düzeyi ad yoktur.

**Demeter yasası** (Law of Demeter) bu erişimi sınırlar. Bir işlev yalnız şunların
yöntemlerini çağırmalıdır: kendi nesnesinin, aldığı parametrelerin, kendi kurduğu nesnelerin
ve doğrudan parçalarının. "Komşunla konuş, komşunun komşusuyla değil" biçiminde özetlenir.
Ölçülebilir karşılığı erişim zincirinin derinliğidir: `gonderi.kod` bir adım, `gonderi.alici.adres.il`
üç adımdır ve aradaki iki yapının biçimine bağımlılık üretir.

## Zincirli Tasarım

Kayıt üç düzey derinliğinde ve beş ayrı istemci onun içine giriyor.

```sh
mkdir -p zincirli kisa
```

```js
// zincirli/kayit.mjs — ic ice gecmis gonderi kaydi
export const KAYIT = {
  kod: "GN-000001",
  alici: { ad: "A. Yilmaz", adres: { il: "Ankara", ilce: "Cankaya", posta: "06500" } },
  tarife: { ad: "agirlik", kademe: { ust: 5, ucret: 6400 } },
};
```

```js
// zincirli/etiket.mjs — alici kaydinin icine iki adim giriyor
export const etiket = (gonderi) =>
  `${gonderi.alici.ad} / ${gonderi.alici.adres.il} ${gonderi.alici.adres.posta}`;
```

```js
// zincirli/ucret.mjs — tarifenin icindeki kademeye ve adresin icine uzaniyor
const BOLGE = { "34": 100, "06": 115, "65": 140 };

export const ucret = (gonderi) =>
  Math.round((gonderi.tarife.kademe.ucret *
    (BOLGE[gonderi.alici.adres.posta.slice(0, 2)] ?? 160)) / 100);
```

```js
// zincirli/rapor.mjs — il ve ilceyi ayri ayri okuyor
export const rapor = (gonderi) =>
  `${gonderi.kod} ${gonderi.alici.adres.il}/${gonderi.alici.adres.ilce} ${gonderi.tarife.ad}`;
```

```js
// zincirli/bildirim.mjs — bildirim metni de ayni zinciri yuruyor
export const bildirim = (gonderi) =>
  `${gonderi.kod} ${gonderi.alici.adres.il} icin yola cikti`;
```

```js
// zincirli/sube.mjs — teslim subesi secimi
export const sube = (gonderi) =>
  `${gonderi.alici.adres.il}-${gonderi.alici.adres.ilce} subesi`;
```

```js
// zincirli/kur.mjs — bes istemcinin ciktisi
import { KAYIT } from "./kayit.mjs";
import { etiket } from "./etiket.mjs";
import { ucret } from "./ucret.mjs";
import { rapor } from "./rapor.mjs";
import { bildirim } from "./bildirim.mjs";
import { sube } from "./sube.mjs";

for (const f of [etiket, ucret, rapor, bildirim, sube]) console.log(f(KAYIT));
```

```sh
node zincirli/kur.mjs
```

```
A. Yilmaz / Ankara 06500
7360
GN-000001 Ankara/Cankaya agirlik
GN-000001 Ankara icin yola cikti
Ankara-Cankaya subesi
```

## Zincir Taraması

Betik her dosyadaki erişim zincirlerini çıkarır, derinliklerini sayar ve iki adımdan uzun
olanları bulgu olarak bildirir. Dil ve çalışma zamanı nesneleri (`Math`, `JSON`, `console`
gibi) alan zinciri sayılmaz; bileşim kökü olan `kur.mjs` de taramanın dışındadır.

```js
// zincir-tara.mjs — erisim zinciri derinligini sayar; iki adimdan uzun her zincir bir bulgu
import { readdirSync, readFileSync } from "node:fs";
import { join } from "node:path";

const ZINCIR = /\b[a-zA-Z_]\w*(?:\.\w+)+/g;
// Dil ve calisma zamani nesneleri alan zinciri sayilmaz.
const DIS = ["Math", "Object", "String", "Number", "JSON", "console", "process", "Array"];

const kok = process.argv[2];
let toplamBulgu = 0;
let enDerin = 0;

for (const d of readdirSync(kok).filter((a) => a.endsWith(".mjs")).sort()) {
  if (d === "kur.mjs") continue;
  const metin = readFileSync(join(kok, d), "utf8").replace(/^\/\/.*$/gm, "");
  const zincirler = [...metin.matchAll(ZINCIR)]
    .map((m) => m[0])
    .filter((z) => !DIS.includes(z.split(".")[0]));
  const derinlikler = zincirler.map((z) => z.split(".").length - 1);
  const bulgular = zincirler.filter((z) => z.split(".").length - 1 >= 2);
  const yerelEnDerin = derinlikler.length === 0 ? 0 : Math.max(...derinlikler);
  toplamBulgu += bulgular.length;
  enDerin = Math.max(enDerin, yerelEnDerin);
  console.log(`  ${d.padEnd(14)} zincir=${zincirler.length} en derin=${yerelEnDerin}` +
    ` bulgu=${bulgular.length}${bulgular.length > 0 ? "  " + bulgular.join(" ") : ""}`);
}
console.log(`${kok.padEnd(9)} toplam bulgu=${toplamBulgu} en derin zincir=${enDerin}`);
```

```sh
node zincir-tara.mjs zincirli
```

```
  bildirim.mjs   zincir=2 en derin=3 bulgu=1  gonderi.alici.adres.il
  etiket.mjs     zincir=3 en derin=3 bulgu=3  gonderi.alici.ad gonderi.alici.adres.il gonderi.alici.adres.posta
  kayit.mjs      zincir=0 en derin=0 bulgu=0
  rapor.mjs      zincir=4 en derin=3 bulgu=3  gonderi.alici.adres.il gonderi.alici.adres.ilce gonderi.tarife.ad
  sube.mjs       zincir=2 en derin=3 bulgu=2  gonderi.alici.adres.il gonderi.alici.adres.ilce
  ucret.mjs      zincir=2 en derin=4 bulgu=2  gonderi.tarife.kademe.ucret gonderi.alici.adres.posta.slice
zincirli  toplam bulgu=11 en derin zincir=4
```

On bir bulgu, en derin zincir dört adım. Beş istemcinin beşi de adres kaydının alan adlarını
biliyor.

## Zincirin Kısaltılması

Zinciri kısaltmanın yolu ara nesnelere davranış vermektir. Adres kaydının biçimini yalnız bir
modül bilir; kalanlar ondan sonuç ister.

```js
// kisa/kayit.mjs — ayni ic ice gecmis kayit
export const KAYIT = {
  kod: "GN-000001",
  alici: { ad: "A. Yilmaz", adres: { il: "Ankara", ilce: "Cankaya", posta: "06500" } },
  tarife: { ad: "agirlik", kademe: { ust: 5, ucret: 6400 } },
};
```

```js
// kisa/adres.mjs — adres kaydinin bicimini yalniz burasi bilir
export const adres = (kayit) => ({
  yaz: () => `${kayit.il} ${kayit.posta}`,
  bolgeKodu: () => kayit.posta.slice(0, 2),
  ilIlce: () => `${kayit.il}/${kayit.ilce}`,
  il: () => kayit.il,
});
```

```js
// kisa/gonderi.mjs — gonderi, komsusuna sorup sonucu kendi adiyla verir
import { adres } from "./adres.mjs";

export const gonderi = (kayit) => {
  const a = adres(kayit.alici.adres);
  return {
    kod: kayit.kod,
    etiketSatiri: () => `${kayit.alici.ad} / ${a.yaz()}`,
    bolgeKodu: () => a.bolgeKodu(),
    ilIlce: () => a.ilIlce(),
    il: () => a.il(),
    tarifeAdi: () => kayit.tarife.ad,
    kademeUcreti: () => kayit.tarife.kademe.ucret,
  };
};
```

```js
// kisa/etiket.mjs — tek adim
export const etiket = (gonderi) => gonderi.etiketSatiri();
```

```js
// kisa/ucret.mjs — tek adim
const BOLGE = { "34": 100, "06": 115, "65": 140 };

export const ucret = (gonderi) =>
  Math.round((gonderi.kademeUcreti() * (BOLGE[gonderi.bolgeKodu()] ?? 160)) / 100);
```

```js
// kisa/rapor.mjs — tek adim
export const rapor = (gonderi) => `${gonderi.kod} ${gonderi.ilIlce()} ${gonderi.tarifeAdi()}`;
```

```js
// kisa/bildirim.mjs — tek adim
export const bildirim = (gonderi) => `${gonderi.kod} ${gonderi.il()} icin yola cikti`;
```

```js
// kisa/sube.mjs — tek adim
export const sube = (gonderi) => `${gonderi.ilIlce().replace("/", "-")} subesi`;
```

```js
// kisa/kur.mjs — ayni bes cikti
import { KAYIT } from "./kayit.mjs";
import { gonderi } from "./gonderi.mjs";
import { etiket } from "./etiket.mjs";
import { ucret } from "./ucret.mjs";
import { rapor } from "./rapor.mjs";
import { bildirim } from "./bildirim.mjs";
import { sube } from "./sube.mjs";

const g = gonderi(KAYIT);
for (const f of [etiket, ucret, rapor, bildirim, sube]) console.log(f(g));
```

```sh
node kisa/kur.mjs
node zincir-tara.mjs kisa
```

```
A. Yilmaz / Ankara 06500
7360
GN-000001 Ankara/Cankaya agirlik
GN-000001 Ankara icin yola cikti
Ankara-Cankaya subesi
  adres.mjs      zincir=6 en derin=2 bulgu=1  kayit.posta.slice
  bildirim.mjs   zincir=2 en derin=1 bulgu=0
  etiket.mjs     zincir=1 en derin=1 bulgu=0
  gonderi.mjs    zincir=10 en derin=3 bulgu=4  kayit.alici.adres kayit.alici.ad kayit.tarife.ad kayit.tarife.kademe.ucret
  kayit.mjs      zincir=0 en derin=0 bulgu=0
  rapor.mjs      zincir=3 en derin=1 bulgu=0
  sube.mjs       zincir=1 en derin=1 bulgu=0
  ucret.mjs      zincir=2 en derin=1 bulgu=0
kisa      toplam bulgu=5 en derin zincir=3
```

Aynı beş çıktı, on bir bulgu yerine beş bulgu. Daha önemlisi bulguların dağılımı: beş istemci
modülün beşi de sıfır bulguya indi, kalan bulgular yapıyı çözen iki modülde toplandı. Demeter
yasası yapının bilinmesini ortadan kaldırmaz, **bilen modül sayısını** düşürür.

## Değişikliğin Dokunduğu Dosyalar

Ölçünün karşılığı bir yapı değişikliğinde çıkar. Adres kaydı yeniden düzenleniyor: `il` ve
`ilce` alanları `konum` adında bir alt kayda taşınıyor. `posta` yerinde kalıyor. Yerinde
düzenlemede yedek uzantısı verilmiştir; GNU ve BSD `sed` bu biçimde aynı davranır.

```sh
cp -r zincirli zincirli-yeni
cp -r kisa kisa-yeni
YENI='{ konum: { il: "Ankara", ilce: "Cankaya" }, posta: "06500" }'
ESKI='{ il: "Ankara", ilce: "Cankaya", posta: "06500" }'
sed -i.y "s#$ESKI#$YENI#" zincirli-yeni/kayit.mjs kisa-yeni/kayit.mjs
sed -i.y 's/\.adres\.il/.adres.konum.il/g' zincirli-yeni/*.mjs
sed -i.y 's/kayit\.il/kayit.konum.il/g' kisa-yeni/adres.mjs
rm -f zincirli-yeni/*.y kisa-yeni/*.y
node zincirli-yeni/kur.mjs
node kisa-yeni/kur.mjs
for k in zincirli kisa; do
  echo "$k: duzenlenen dosya = $(diff -rq $k $k-yeni | grep -c '^Files')  ->" \
    "$(diff -rq $k $k-yeni | sed 's#.*/\([a-z]*\.mjs\) .*#\1#' | tr '\n' ' ')"
done
```

```
A. Yilmaz / Ankara 06500
7360
GN-000001 Ankara/Cankaya agirlik
GN-000001 Ankara icin yola cikti
Ankara-Cankaya subesi
A. Yilmaz / Ankara 06500
7360
GN-000001 Ankara/Cankaya agirlik
GN-000001 Ankara icin yola cikti
Ankara-Cankaya subesi
zincirli: duzenlenen dosya = 5  -> bildirim.mjs etiket.mjs kayit.mjs rapor.mjs sube.mjs
kisa: duzenlenen dosya = 2  -> adres.mjs kayit.mjs
```

İki sürüm de aynı beş satırı üretmeye devam ediyor. Fark maliyette: aynı yapı değişikliği
zincirli tasarımda beş dosyayı, kısa tasarımda iki dosyayı düzenletti. Zincirli sürümde ücret
modülü dokunulmayan tek modül oldu, çünkü yalnız `posta` alanını kullanıyordu — yani hangi
modülün etkileneceği, o modülün hangi alan adına eriştiğine bağlıydı.

## Yasanın Yanlış Uygulanışı

Zinciri kısaltmanın yanlış yolu, her ara alan için bir aktarıcı yöntem yazmaktır.
`gonderi.aliciAdi()`, `gonderi.aliciIl()`, `gonderi.aliciIlce()`, `gonderi.aliciPosta()`
biçiminde büyüyen bir arayüz zinciri kısaltır ama aynı bilgiyi bu kez yöntem adlarına
kopyalar; alan eklendikçe arayüz büyür ve bir önceki derste ölçülen arayüz fazlalığı geri
gelir. Yukarıdaki kısa sürümde `adres` nesnesi ham alan döndürmüyor, sorulan soruya cevap
veriyor: `yaz`, `bolgeKodu`, `ilIlce`. Bunlar alan adlarının değil, istemcilerin
sorularının adlarıdır.

Yasanın istisnası da vardır: veri taşımak için var olan kayıtlarda — sıralı bir yapılandırma
nesnesinde ya da bir sınır noktasında ayrıştırılmış gövdede — zincir kaçınılmazdır ve
maliyeti düşüktür, çünkü o kayıt zaten dış bir sözleşmedir. Ölçü, zincirin varlığı değil,
kaç modülün aynı yapıyı ayrı ayrı bilmesidir.

## Özet

- Demeter yasası bir işlevin erişebileceği nesneleri kendi parçaları, parametreleri ve kurduğu
  nesnelerle sınırlar; ölçüsü erişim zincirinin adım sayısıdır.
- Beş istemcinin kayda doğrudan girdiği tasarımda 11 bulgu ve en derin 4 adımlık zincir
  sayıldı.
- Ara nesnelere davranış verilince bulgu sayısı 5'e indi ve beş istemci modülün beşi de sıfır
  bulguya düştü; kalan bulgular yapıyı çözen iki modülde toplandı.
- Adres kaydı yeniden yapılandırıldığında zincirli tasarımda 5, kısa tasarımda 2 dosya
  düzenlendi; iki sürüm de aynı beş satırı üretmeye devam etti.
- Zinciri her alan için bir aktarıcı yöntemle kısaltmak ölçüyü düzeltmez; ara nesne alan
  adlarını değil, istemcilerin sorularını karşılamalıdır.

## Sonraki Adım

Kısa sürümde `adres` nesnesi hâlâ değer döndürüyor: `ilIlce()` bir dize verir, kararı çağıran
verir. Şube seçimi bu dizeyi alıp biçimlendiriyor, bildirim başka bir parçasını kullanıyor.
Aynı kural birden çok çağrı yerinde tekrarlandığında soru değişir: veriyi dışarı verip kararı
istemciye bırakmak mı, yoksa kararı nesneye söylemek mi? Sonraki ders aynı kuralın kaç ayrı
yerde tekrarlandığını sayar ve kararı veriye taşıyan sürümle karşılaştırır.
