---
title: 'Tip Nesnesi ve Nitelik Varyantları'
source: 'https://academia.sh/tr/kurslar/tasarim-kaliplari/tip-nesnesi-ve-nitelik-varyantlari'
course: 'Tasarım Kalıpları'
language: tr
updated: '2026-08-17T18:10:39+00:00'
license: 'CC BY-SA 4.0'
---

# Tip Nesnesi ve Nitelik Varyantları

Türün çalışma zamanında modellenmesi: her hizmet türü için sınıf yazan düzen ile türü veri olarak taşıyan tip nesnesinin karşılaştırılması, tür adı taşıyan kod dosyası sayısının ve yeni tür eklemenin maliyetinin sayılması, nitelik torbasının çalışma zamanında doğrulanması ve kaybedilen yazım zamanı denetiminin bedeli.

Buraya kadarki altı kalıp gönderi türünün tek olduğunu varsaydı. Kitaplığa ikinci bir hizmet
türü girdiğinde — soğuk zincir gönderisi, değerli eşya gönderisi, belge gönderisi — her türün
kendi nitelikleri ve kendi ücret çarpanı olur. Türü koda yazmak, tür sayısı arttıkça
düzenlenen dosya sayısını artıran bir karardır; türler bir veri kaynağından geliyorsa kod
yazmak zaten olanaksızdır.

**Tip nesnesi** (type object) türü bir sınıf olarak değil, bir nesne olarak modeller: türün
adı, gerektirdiği nitelikler ve ücret davranışı veri hâline gelir. **Nitelik varyantları** ise
bunun ikinci yarısıdır: her türün farklı bir nitelik kümesi taşıması, gönderi kaydının sabit
alanlar yerine bir **nitelik torbası** (property bag) tutmasıyla çözülür ve torbanın geçerliliği
tip nesnesine sorulur.

## Sınıf Başına Tür

İlk düzende her hizmet türü bir sınıftır; nitelik listesi ve çarpan sınıfın içinde durur.

```js
// siniflar/turler.mjs — her hizmet turu bir sinif; nitelik listesi ve carpan kodda
export class Standart {
  static nitelikler = [];
  ucret(g) { return Math.round(g.taban); }
}

export class SogukZincir {
  static nitelikler = ["sicaklik"];
  ucret(g) { return Math.round(g.taban * 1.6); }
}

export class Degerli {
  static nitelikler = ["sigortaDegeri"];
  ucret(g) { return Math.round(g.taban * 1.25 + g.nitelikler.sigortaDegeri * 0.01); }
}
```

```js
// siniflar/kayit.mjs — tur anahtarini sinifa baglayan kayit
import { Standart, SogukZincir, Degerli } from "./turler.mjs";

const KAYIT = { "standart": Standart, "soguk-zincir": SogukZincir, "degerli": Degerli };

export function ucret(g) {
  const Tur = KAYIT[g.tur];
  if (Tur === undefined) throw new RangeError(`bilinmeyen hizmet turu: ${g.tur}`);
  return new Tur().ucret(g);
}
```

Düzenin kazancı her türün davranışını kendi metninde tutmasıdır. Kaybı, tür kümesinin kodun
bir parçası olmasıdır: yeni tür iki dosyayı da düzenlemek anlamına gelir.

## Tip Nesnesi

İkinci düzende tür bir nesnedir. Kod hiçbir tür adını bilmez; yalnız bir türün nasıl bir şey
olduğunu bilir.

```js
// tip/tip-nesnesi.mjs — tur bir nesne olarak modellenir; kodda hicbir tur adi gecmez
export class TipNesnesi {
  constructor({ ad, nitelikler, carpan, ek }) {
    Object.assign(this, { ad, nitelikler, carpan, ek: ek ?? null });
  }
  dogrula(g) {
    const torba = g.nitelikler ?? {};
    return [
      ...this.nitelikler.filter((n) => n in torba === false).map((n) => `eksik nitelik: ${n}`),
      ...Object.keys(torba).filter((n) => this.nitelikler.includes(n) === false).map((n) => `taninmayan nitelik: ${n}`),
    ];
  }
  ucret(g) {
    const ek = this.ek === null ? 0 : (g.nitelikler?.[this.ek.alan] ?? 0) * this.ek.oran;
    return Math.round(g.taban * this.carpan + ek);
  }
}

export const tipKatalogu = (json) => new Map(JSON.parse(json).map((t) => [t.ad, new TipNesnesi(t)]));
```

Türlerin kendisi veridir. Aşağıdaki dosya bir kod dosyası değil, veri taşıyan bir dosyadır;
aynı metin bir yapılandırma kaydından ya da bir tablodan da gelebilirdi.

```js
// tip/tur-verisi.mjs — hizmet turleri veri olarak: her satir bir tur
export const TUR_JSON = `[
  { "ad": "standart", "nitelikler": [], "carpan": 1 },
  { "ad": "soguk-zincir", "nitelikler": ["sicaklik"], "carpan": 1.6 },
  { "ad": "degerli", "nitelikler": ["sigortaDegeri"], "carpan": 1.25, "ek": { "alan": "sigortaDegeri", "oran": 0.01 } }
]`;
```

`ek` alanı nitelik varyantlarının ücrete karışma biçimidir: değerli eşyada sigorta değerinin
yüzde biri ücrete eklenir, öteki türlerde böyle bir kural yoktur. Sınıf tabanlı düzende bu
kural bir yöntem gövdesiydi; burada iki alanlı bir kayıt.

## Ölçüm

Üç şey sayılır: tür adının kaç kod dosyasında durduğu, yeni bir türün eklenmesinin neye mal
olduğu ve geçersiz niteliklerin kaçının yakalandığı.

```js
// tur-say.mjs — tur adinin kac kod dosyasinda durdugu ve yeni turun eklenme maliyeti
import { readFileSync } from "node:fs";
import { ucret as sinifUcreti } from "./siniflar/kayit.mjs";
import { tipKatalogu } from "./tip/tip-nesnesi.mjs";
import { TUR_JSON } from "./tip/tur-verisi.mjs";

const TUR_ADI = /Standart|SogukZincir|Degerli|"(standart|soguk-zincir|degerli)"/;
const KOD = { "sinif tabanli": ["siniflar/turler.mjs", "siniflar/kayit.mjs"], "tip nesnesi ": ["tip/tip-nesnesi.mjs"] };

for (const [ad, dosyalar] of Object.entries(KOD)) {
  const metinler = dosyalar.map((d) => readFileSync(d, "utf8"));
  const dokunan = metinler.filter((m) => TUR_ADI.test(m)).length;
  const liste = metinler.reduce((t, m) => t + [...m.matchAll(/static nitelikler|"nitelikler"/g)].length, 0);
  console.log(`${ad}: tur adi tasiyan kod dosyasi = ${dokunan} / ${dosyalar.length}, kodda nitelik listesi = ${liste}`);
}
console.log(`tur verisi  : ${JSON.parse(TUR_JSON).length} tur, tip/tur-verisi.mjs veri dosyasidir`);

const BELGE = `{ "ad": "belge", "nitelikler": ["sayfa"], "carpan": 0.7 }`;
const katalog = tipKatalogu(`[${TUR_JSON.slice(1, -1).trim()},\n  ${BELGE}\n]`);
const g = { tur: "belge", taban: 8640, nitelikler: { sayfa: 4 } };

let sinifSonuc;
try { sinifSonuc = String(sinifUcreti(g)); } catch (e) { sinifSonuc = `hata: ${e.message}`; }
console.log(`\nyeni tur "belge" eklendi (1 veri satiri, 0 kod satiri)`);
console.log(`  sinif tabanli -> ${sinifSonuc}`);
console.log(`  tip nesnesi   -> ${katalog.get("belge").ucret(g)}, katalogdaki tur sayisi = ${katalog.size}`);

const HATALILAR = [
  { tur: "soguk-zincir", taban: 8640, nitelikler: {} },
  { tur: "degerli", taban: 8640, nitelikler: { sigortaDegeri: 50000, sicaklik: -18 } },
  { tur: "standart", taban: 8640, nitelikler: { sayfa: 4 } },
];
let yakalanan = 0, sinifYakalanan = 0;
for (const h of HATALILAR) {
  const bulgular = katalog.get(h.tur).dogrula(h);
  if (bulgular.length > 0) yakalanan += 1;
  let sinif;
  try { sinif = String(sinifUcreti(h)); } catch (e) { sinif = `hata: ${e.name}`; sinifYakalanan += 1; }
  console.log(`  ${h.tur.padEnd(13)} tip nesnesi: ${bulgular.join("; ") || "bulgu yok"}  | sinif tabanli: ${sinif}`);
}
console.log(`dogrulama: tip nesnesi ${yakalanan}/${HATALILAR.length}, sinif tabanli ${sinifYakalanan}/${HATALILAR.length}`);
```

```sh
node tur-say.mjs
```

```
sinif tabanli: tur adi tasiyan kod dosyasi = 2 / 2, kodda nitelik listesi = 3
tip nesnesi : tur adi tasiyan kod dosyasi = 0 / 1, kodda nitelik listesi = 0
tur verisi  : 3 tur, tip/tur-verisi.mjs veri dosyasidir

yeni tur "belge" eklendi (1 veri satiri, 0 kod satiri)
  sinif tabanli -> hata: bilinmeyen hizmet turu: belge
  tip nesnesi   -> 6048, katalogdaki tur sayisi = 4
  soguk-zincir  tip nesnesi: eksik nitelik: sicaklik  | sinif tabanli: 13824
  degerli       tip nesnesi: taninmayan nitelik: sicaklik  | sinif tabanli: 11300
  standart      tip nesnesi: taninmayan nitelik: sayfa  | sinif tabanli: 8640
dogrulama: tip nesnesi 3/3, sinif tabanli 0/3
```

İlk iki satır kazancı veriyor: sınıf tabanlı düzende iki kod dosyasının ikisi de tür adı
taşıyor ve nitelik listesi kodda üç yerde duruyor. Tip nesnesi düzeninde tür adı taşıyan kod
dosyası 0, kodda nitelik listesi 0; üçünün üçü de veriye geçmiş durumda.

Dördüncü tür bunun sonucudur. `belge` türü koda hiç girmeden eklendi: 1 veri satırı, 0 kod
satırı. Sınıf tabanlı düzen aynı gönderiyi ücretlendiremedi ve bilinmeyen tür hatası verdi.
Katalog dört türe çıktı ve yeni türün ücreti hesaplandı.

Son üç satır bedeli gösteriyor. Sınıf tabanlı düzen üç hatalı gönderinin üçünü de sessizce
ücretlendirdi: eksik nitelikli soğuk zincir gönderisi 13824, tanınmayan nitelik taşıyan iki
gönderi de bir tutar üretti. Tip nesnesi üçünü de yakaladı, ama bunu **çalışma zamanında** ve
kendi yazdığı doğrulamayla yaptı.

## Bedel ve Ne Zaman Uygulanmaz

Tip nesnesinin ödediği şey yazım zamanında var olan denetimdir. Sınıf tabanlı düzende bir tür
için yanlış bir nitelik adı yazıldığında hata sınıfın metninde görünür; tip nesnesi düzeninde
nitelik adları dizgeye dönüşür ve hiçbir okuma sırasında denetlenmez. Bu yüzden kalıp bir
doğrulama yükümlülüğü getirir: yukarıdaki ölçümde üç bulgu doğrulama işlevi yazıldığı için
yakalandı, kalıbın kendisi yakalamadı.

İkinci bedel çağrı yerinin belirsizleşmesidir. Sınıf tabanlı düzende bir türün ücret
hesabını okumak için o türün sınıfına gidilir; tip nesnesi düzeninde tek bir `ucret` gövdesi
bütün türleri hesaplar ve türe özgü davranış `ek` gibi alanlarla ifade edilir. Türe özgü
davranış karmaşıklaştıkça bu alanların sayısı artar ve tip nesnesi kendi içinde küçük bir
yorumlayıcıya dönüşmeye başlar.

Buradan uygulanma sınırı çıkar. Tür sayısı sabit ve azsa, her türün davranışı gerçekten farklı
bir algoritmaysa, kalıp karşılıksızdır: nitelik listesi zaten 3 yerde durur ve yeni tür
gelmediği için düzenlenen dosya sayısı 0 kalır. Bu durumda türü çok biçimlilikle ifade etmek
hem yazım zamanı denetimini korur hem davranışı kendi dosyasında tutar.

Ters koşullar kalıbı gerektirir: tür kümesi çalışma zamanında geliyorsa, kullanıcı yeni tür
tanımlıyorsa, ya da türler yalnız veriyle ayrışıp davranışları ortak bir kuralın
parametreleriyse. Ölçüt tek cümledir: türler arasındaki fark **veriyle** ifade edilebiliyorsa
tip nesnesi, **kodla** ifade edilmek zorundaysa çok biçimlilik.

## Özet

- Tip nesnesi türü bir sınıf olarak değil bir nesne olarak modeller; nitelik varyantları
  türe göre değişen alanları bir nitelik torbasında toplar ve geçerliliğini tip nesnesine
  sorar.
- Sınıf tabanlı düzende iki kod dosyasının ikisi de tür adı taşıyordu ve nitelik listesi kodda
  3 yerde duruyordu; tip nesnesi düzeninde ikisi de 0'a indi.
- Dördüncü tür 1 veri satırı ve 0 kod satırıyla eklendi ve ücreti hesaplandı; sınıf tabanlı
  düzen aynı gönderi için bilinmeyen tür hatası verdi.
- Bedel yazım zamanı denetiminin kaybıdır: üç hatalı gönderiyi sınıf tabanlı düzen sessizce
  ücretlendirdi, tip nesnesi üçünü de yakaladı ama bunu elle yazılmış bir doğrulamayla yaptı.
- Ölçüt türler arasındaki farkın nerede ifade edilebildiğidir: veriyle ifade edilebiliyorsa
  tip nesnesi, kodla ifade edilmek zorundaysa çok biçimlilik.

## Kurs Kapanışı

Kurs dört problem sınıfını dolaştı. **Yaratımsal kalıplar** nesnenin nasıl ve nerede
yaratıldığını sordu: Kalıp Kavramı dersi kalıbın anlatısını problem, çözüm ve sonuçlar
üçlüsü olarak kurdu ve ilk ölçümü verdi — tür adının geçtiği yer sayısı 6'dan 2'ye inerken
dosya sayısı 3'ten 5'e, ithal kenarı sayısı 2'den 7'ye çıktı. **Yapısal kalıplar** nesnelerin
nasıl birleştirildiğini ve uyumsuz arayüzlerin nasıl köprülendiğini sordu; Bağdaştırıcı dersi
kazancı dış sözlüğe dokunan dosya oranıyla, bedeli 3 dosya, 4 satır ve bir dolaylılık
basamağıyla saydı. **Davranışsal kalıplar** sorumluluğun nesneler arasında nasıl dağıtıldığını
sordu; Strateji dersinde dördüncü tarife gömülü sürümde 2 dosya ve 13 satır düzenlemeye mal
olurken strateji sürümünde 0 dosya düzenlemesiyle eklendi, karşılığında izlenmesi gereken
gövde sayısı 1'den 3'e çıktı.

**Kurumsal uygulama kalıpları** ölçeği bir uygulamanın katman düzenine çıkardı. Aynı ölçüm
biçimi orada da yürüdü: kural uygulama noktası 3'ten 1'e, alan modülündeki kalıcılık izi
dört, iki ve beş adımdan 0'a, uygulama katmanındaki tekrarlanan senaryo adımı 7'den 0'a, iç
model değiştiğinde bozulan istemci sayısı 3'ten 0'a, tür adı taşıyan kod dosyası 2'den 0'a
indi. Her derste bedel de aynı cümleyle sayıldı: eklenen dosya, eklenen ad, dolaylılık
basamağı, izlenmesi gereken çağrı.

Kursun başında kurulan iddia budur: **kalıp, ölçüyü iyileştirdiği ölçüde kalıptır.** Otuz
dersin hiçbirinde kalıp kendiliğinden iyi sayılmadı; her biri bir sayıyı düşürdüğü için
seçildi ve her birinin "ne zaman uygulanmaz" bölümü aynı sayının nasıl sıfırlandığını
gösterdi. Kazanç ile bedelin oranı bağlama bağlıdır ve bağlam ölçülebilir: kaç senaryo aynı
kurala dokunuyor, kaç istemci aynı sınırdan geçiyor, kaç tür bekleniyor. Bu sorular
yanıtlanmadan bir kalıp seçilemez.

Kalıplar bir soruyu açıkta bıraktı. Hepsi çözümün **biçimi** üzerineydi: kural nereye yazılır,
nesne nasıl yaratılır, sınır nereye çizilir. Hiçbiri kuralın **ne olduğunu** sormadı. Gönderi,
tarife, bölge, rota ve indirim sözcüklerinin ne anlama geldiği, hangilerinin bir arada
değiştiği, aynı sözcüğün iki takımda iki farklı şeyi adlandırıp adlandırmadığı bu kursun
dışında kaldı. Sonraki kurs **Alan Odaklı Tasarım** tam bu soruyu alır: alan dilinin ve
sınırlı bağlamların birer modelleme aracı hâline gelmesi, yani bir kod tabanının yapısının
alanın kendi ayrımlarından türetilmesi.
