---
title: 'Sinek Siklet'
source: 'https://academia.sh/tr/kurslar/tasarim-kaliplari/sinek-siklet'
course: 'Tasarım Kalıpları'
language: tr
updated: '2026-08-17T18:10:39+00:00'
license: 'CC BY-SA 4.0'
---

# Sinek Siklet

Paylaşımla bellek tasarrufu: toplu ücretlendirmede her gönderinin kendi tarife kopyasını taşıdığı düzen ile değişmeyen tarife bilgisini paylaşılan nesnede toplayan düzenin nesne sayısı ve işaretlenmiş yığın ölçümüyle karşılaştırılması, paylaşımın değiştirilemezlik zorunluluğu ve paylaşım genişliği cinsinden bedeli.

Önceki dört kalıpta ölçü dosya, tip ve bağ sayısıydı; hepsi kaynak metnin büyüklüğüne
bakıyordu. Bu dersin ölçüsü çalışma zamanındadır: bellek.

Toplu ücretlendirme işi iki yüz bin gönderi satırını işliyor. Her gönderi bir tarife bilgisine
ihtiyaç duyar: ağırlık kademeleri tablosu, bölge katsayıları, para birimi. Bu bilginin içeriği
gönderiye göre **değişmez** — aynı tarife kodunu taşıyan bütün gönderilerde birebir aynıdır.
Gönderiye göre değişen şey ağırlık ve adrestir. **Sinek siklet** (flyweight) kalıbı bu ikisini
ayırır: değişmeyen kısım **iç durum** olarak tek bir nesnede paylaşılır, değişen kısım **dış
durum** olarak kaydın kendisinde kalır. Ölçüler kurulan nesne sayısı ve yığın kullanımıdır.

## Problem: Aynı Tablonun İki Yüz Bin Kopyası

Ham satırlar ve tarife tabloları iki düzende de ortaktır. Satırlar dizinden türetildiği için
her koşumda aynıdır; rastgelelik yoktur.

```js
// veri.mjs — ham gonderi satirlari ve tarife tablolari; degerler dizinden turetilir
export const KADEME_TABLO = {
  standart: [[1, 4990], [5, 8490], [15, 14990], [30, 24990]],
  ekonomik: [[1, 3990], [5, 6990], [15, 11990], [30, 19990]],
  hizli: [[1, 6490], [5, 10990], [15, 18990], [30, 31990]],
};
export const KATSAYI_TABLO = { 34: 100, "06": 118, 35: 120, 65: 145 };
export const TARIFE_KODU = ["standart", "ekonomik", "hizli"];
const POSTA = ["34100", "06500", "35400", "65100"];

export function* hamSatirlar(n) {
  for (let i = 0; i < n; i += 1) {
    yield {
      kod: `G-${i}`,
      agirlik: 0.5 + (i % 59) / 2,
      postaKodu: POSTA[i % POSTA.length],
      tarifeKodu: TARIFE_KODU[i % TARIFE_KODU.length],
    };
  }
}
```

İlk düzende her kayıt kendi tarife nesnesini kurar. Bu, tabloları paylaşmamanın en doğal
yoludur: kayıt kendi kendine yeter, hiçbir şeyi ortak kullanmaz.

```js
// kopyali/kur.mjs — her gonderi kendi tarife kopyasini tasir
import { KADEME_TABLO, KATSAYI_TABLO } from "../veri.mjs";

let uretilen = 0;
export const uretilenTarife = () => uretilen;

export function kur(satirlar) {
  const liste = [];
  for (const s of satirlar) {
    uretilen += 1;
    liste.push({
      kod: s.kod,
      agirlik: s.agirlik,
      postaKodu: s.postaKodu,
      tarife: {
        kod: s.tarifeKodu,
        kademeler: KADEME_TABLO[s.tarifeKodu].map(([u, f]) => [u, f]),
        katsayilar: { ...KATSAYI_TABLO },
        paraBirimi: "kurus",
      },
    });
  }
  return liste;
}

export function ucret(g) {
  const k = g.tarife.kademeler.find(([ust]) => g.agirlik <= ust) ?? [0, 24990];
  return Math.round((k[1] * (g.tarife.katsayilar[g.postaKodu.slice(0, 2)] ?? 165)) / 100);
}
```

## Çözüm: İç Durumu Paylaşan Bir Havuz

Sinek siklet fabrikası aynı iç durum için tek nesne döndürür. Nesne **değiştirilemez** olmak
zorundadır; gerekçesi aşağıda ölçülüyor.

```js
// paylasimli/tarife.mjs — sinek siklet fabrikasi: ayni tarife kodu icin tek nesne
import { KADEME_TABLO, KATSAYI_TABLO } from "../veri.mjs";

const havuz = new Map();
let uretilen = 0;

export const uretilenTarife = () => uretilen;
export const havuzBoyu = () => havuz.size;

export function tarifeAl(kod) {
  let t = havuz.get(kod);
  if (t === undefined) {
    uretilen += 1;
    t = Object.freeze({
      kod,
      kademeler: Object.freeze(KADEME_TABLO[kod].map(([u, f]) => Object.freeze([u, f]))),
      katsayilar: Object.freeze({ ...KATSAYI_TABLO }),
      paraBirimi: "kurus",
    });
    havuz.set(kod, t);
  }
  return t;
}
```

```js
// paylasimli/kur.mjs — gonderi tarifeye basvuru tutar; ic durum paylasilir, dis durum satirda kalir
import { tarifeAl, uretilenTarife, havuzBoyu } from "./tarife.mjs";

export { uretilenTarife, havuzBoyu };

export function kur(satirlar) {
  const liste = [];
  for (const s of satirlar) {
    liste.push({ kod: s.kod, agirlik: s.agirlik, postaKodu: s.postaKodu, tarife: tarifeAl(s.tarifeKodu) });
  }
  return liste;
}

export function ucret(g) {
  const k = g.tarife.kademeler.find(([ust]) => g.agirlik <= ust) ?? [0, 24990];
  return Math.round((k[1] * (g.tarife.katsayilar[g.postaKodu.slice(0, 2)] ?? 165)) / 100);
}
```

Ücret hesabının gövdesi iki düzende aynıdır; değişen şey `g.tarife`nin kaça ait olduğudur.

```js
// kosum.mjs — iki duzen ayni toplam ucreti mi veriyor, kac tarife nesnesi kuruluyor
import { hamSatirlar } from "./veri.mjs";
import * as kopyali from "./kopyali/kur.mjs";
import * as paylasimli from "./paylasimli/kur.mjs";

const N = 20000;
const topla = (duzen) => duzen.kur(hamSatirlar(N)).reduce((t, g) => t + duzen.ucret(g), 0);

const a = topla(kopyali), b = topla(paylasimli);
console.log(`kopyali   : toplam=${a}  tarife nesnesi=${kopyali.uretilenTarife()}`);
console.log(`paylasimli: toplam=${b}  tarife nesnesi=${paylasimli.uretilenTarife()}`);
console.log(`toplam farki = ${a - b}  gonderi = ${N}`);
```

```
kopyali   : toplam=463093345  tarife nesnesi=20000
paylasimli: toplam=463093345  tarife nesnesi=3
toplam farki = 0  gonderi = 20000
```

Yirmi bin gönderi için kopyalı düzen 20.000 tarife nesnesi kurdu, paylaşımlı düzen 3. Toplam
ücret kuruşu kuruşuna aynı. Nesne sayısı ortamdan bağımsız, kesin bir sayıdır: iç durum çeşidi
kadar nesne kurulur.

## Yığın Ölçümü

İkinci ölçü yığın kullanımıdır ve **ortama bağlıdır**: aşağıdaki megabayt değerleri bu
makinede `node` v24.18.0 ile alınmıştır; sürüm, işletim sistemi ve çöp toplayıcı ayarları
değiştiğinde sayılar değişir. Değişmeyen şey büyüklük mertebesidir. Ölçüm için çöp toplayıcının
elle çağrılabilmesi gerekir; bu yüzden betik `--expose-gc` seçeneğiyle çalıştırılır.

```js
// bellek.mjs — bir duzeni kurup yigin kullanimini olcer; node --expose-gc ile calistirilir
import { hamSatirlar } from "./veri.mjs";

if (globalThis.gc === undefined) {
  console.error("bu betik 'node --expose-gc bellek.mjs <duzen> <adet>' ile calistirilir");
  process.exit(1);
}

const duzen = process.argv[2];
const adet = Number(process.argv[3]);
const { kur, ucret, uretilenTarife } = await import(`./${duzen}/kur.mjs`);

globalThis.gc();
const once = process.memoryUsage().heapUsed;
const liste = kur(hamSatirlar(adet));
globalThis.gc();
const sonra = process.memoryUsage().heapUsed;
const toplam = liste.reduce((t, g) => t + ucret(g), 0);
console.log(`${duzen.padEnd(11)} gonderi=${liste.length}  tarife nesnesi=${uretilenTarife()}  ` +
  `yigin=${Math.round((sonra - once) / 1048576)} MB  toplam=${toplam}`);
```

```sh
node --expose-gc bellek.mjs kopyali 200000
node --expose-gc bellek.mjs paylasimli 200000
```

```
kopyali     gonderi=200000  tarife nesnesi=200000  yigin=208 MB  toplam=4631039370
paylasimli  gonderi=200000  tarife nesnesi=3  yigin=20 MB  toplam=4631039370
```

İki yüz bin gönderi için 208 megabayta karşı 20 megabayt; oran on katın biraz üstünde. Kalan
20 megabayt dış durumun kendisidir — gönderi kodu, ağırlık, posta kodu — ve paylaşımla
küçültülemez, çünkü her kayıtta farklıdır. Ölçüm iki ayrı süreçte yapıldı; aynı süreçte iki
düzeni birden kurmak, birinci düzenin bıraktığı nesnelerin ikinci ölçüme karışması riskini
taşır.

Toplam ücret her iki koşumda da aynı çıktı; paylaşım sonucu değiştirmedi.

## Bedel: Değiştirilemezlik ve Paylaşımın Genişliği

Paylaşılan nesnenin en görünür bedeli, artık kimseye ait olmamasıdır.

```js
// bedel.mjs — paylasimin bedeli: degistirilebilirlik, paylasim genisligi, nesne kimligi
import { hamSatirlar } from "./veri.mjs";
import * as kopyali from "./kopyali/kur.mjs";
import * as paylasimli from "./paylasimli/kur.mjs";

const N = 20000;
const k = kopyali.kur(hamSatirlar(N));
const p = paylasimli.kur(hamSatirlar(N));

const oncekiK = k.map(kopyali.ucret);
k[0].tarife.katsayilar["34"] = 300;
console.log(`kopyali   : bir kaydin tablosu degisti -> degisen sonuc = ` +
  `${k.map(kopyali.ucret).filter((v, i) => v !== oncekiK[i]).length} / ${N}`);

try {
  p[0].tarife.katsayilar["34"] = 300;
  console.log("paylasimli: ayni degisiklik kabul edildi");
} catch (e) {
  console.log(`paylasimli: ayni degisiklik reddedildi (${e.constructor.name})`);
}

console.log(`paylasimli: ayni nesneyi paylasan gonderi = ${p.filter((g) => g.tarife === p[0].tarife).length} / ${N}`);
console.log(`kopyali   : ayni nesneyi paylasan gonderi = ${k.filter((g) => g.tarife === k[0].tarife).length} / ${N}`);
const cesit = paylasimli.havuzBoyu();
console.log(`ic durum cesidi = ${cesit}  paylasim orani = ${Math.round(N / cesit)} gonderi/nesne`);
```

```
kopyali   : bir kaydin tablosu degisti -> degisen sonuc = 1 / 20000
paylasimli: ayni degisiklik reddedildi (TypeError)
paylasimli: ayni nesneyi paylasan gonderi = 6667 / 20000
kopyali   : ayni nesneyi paylasan gonderi = 1 / 20000
ic durum cesidi = 3  paylasim orani = 6667 gonderi/nesne
```

Kopyalı düzende bir kaydın tablosunu değiştirmek yalnız o kaydı bozdu: 1/20000. Paylaşımlı
düzende aynı değişiklik `Object.freeze` sayesinde reddedildi ve `TypeError` verdi. Dondurma
olmasaydı değişiklik kabul edilecek ve aynı nesneyi paylaşan 6667 gönderinin ücreti sessizce
sapacaktı. Paylaşım oranı hem kazancın hem riskin ölçüsüdür: her nesne 6667 kayda hizmet
ediyor, dolayısıyla o nesnedeki bir hata 6667 kayda yayılır.

İkinci bedel nesne kimliğinin anlam değiştirmesidir. Kopyalı düzende `g1.tarife === g2.tarife`
karşılaştırması yalnız aynı kayıt için doğrudur; paylaşımlı düzende aynı tarife kodunu taşıyan
her gönderi için doğrudur. Kimlik eşitliğine dayanarak "bu iki gönderi aynı tarife nesnesini
kullanıyorsa aynı müşteriye aittir" gibi bir çıkarım yapan kod, paylaşıma geçildiğinde yanılır.

Üçüncü bedel havuzun kendisidir: bir dosya, bir eşleme yapısı ve her kayıt kurulurken bir arama.
Havuz ayrıca hiçbir zaman boşalmaz; iç durum çeşidi sınırsızsa havuz sınırsız büyür.

Buradan uygulanmama koşulu doğrudan çıkar. Kazanç, kayıt sayısının iç durum çeşidine oranıyla
büyür: bu koşumda 200.000/3. Oran 1'e yaklaşırsa — her kaydın iç durumu kendine özgüyse —
paylaşılacak hiçbir şey yoktur, havuz kayıt sayısı kadar nesne tutar ve üstüne eşleme yükü
eklenir. Kayıt sayısı küçükse (yüzlerce) 200 megabaytlık fark yüz kilobayta iner ve
değiştirilemezlik kısıtı karşılıksız kalır.

## Özet

- Sinek sikletin çözdüğü problem, kayıt sayısı kadar kopyalanan ama kayda göre değişmeyen
  bilginin belleği doldurmasıdır; çözüm iç durumu paylaşılan nesnede toplamak, dış durumu
  kayıtta bırakmaktır.
- Yirmi bin gönderi için kurulan tarife nesnesi 20.000'den 3'e indi ve toplam ücret kuruşu
  kuruşuna aynı kaldı; nesne sayısı ortamdan bağımsız bir ölçüdür.
- İki yüz bin gönderide yığın kullanımı 208 megabayttan 20 megabayta indi; bu değerler bu
  makinede `node` v24.18.0 ile ölçüldü ve ortam değiştiğinde değişir, oran ise mertebe olarak
  korunur.
- Paylaşılan nesne değiştirilemez olmak zorundadır: kopyalı düzende bir tablo değişikliği 1
  kaydı bozdu, paylaşımlı düzende aynı değişiklik dondurma sayesinde reddedildi; kabul edilseydi
  aynı nesneyi paylaşan 6667 kaydı bozacaktı.
- Kazanç kayıt sayısının iç durum çeşidine oranıyla büyür; oran 1'e yakınsa ya da kayıt sayısı
  küçükse kalıp yalnız havuz yükü ve değiştirilemezlik kısıtı ekler.

## Sonraki Adım

Sinek siklette bir nesneye çok sayıda kayıt aynı başvuruyla ulaştı; nesne paylaşıldı ama
erişim serbestti — isteyen `tarife` alanını okuyup doğrudan kullanabiliyordu. Bir sonraki
problem erişimin kendisini konu edinir. Tarife tablosu uzak bir kaynaktan geliyorsa ilk erişime
kadar hiç yüklenmemesi, aynı tablonun ikinci kez istenmesinde tekrar getirilmemesi ve hangi
kaydın hangi tabloyu istediğinin sayılabilmesi gerekir. Üçü de asıl nesnenin gövdesine
dokunmadan, aynı arayüzü sunan bir nesnenin araya girmesiyle çözülür. Sonraki ders kaynağa giden
çağrı sayısını iki düzende sayar, vekil kalıbını yazar ve bedelini bir dolaylılık basamağı ile
bayat veri riski cinsinden ölçer.
