İçeriğe geç
Çılgın Yazılım

Yazılım Geliştirme kod örnekli

Değişmezlik (Immutability): Kopyalamak Neden Bazen Daha Hızlıdır?

Değiştirilemeyen nesneler bellek israfı gibi görünür; oysa çoğu hatayı doğmadan öldürür. Nerede kullanmalı, nerede kaçınmalı?

Çılgın Yazılım 3 dk okuma

Değişmezlik (Immutability): Kopyalamak Neden Bazen Daha Hızlıdır?
İçindekiler
  1. Mutasyonun sessiz maliyeti
  2. PHP 8.1+ ile değişmez nesneler
  3. JavaScript tarafı: const yeterli değil
  4. "Her değişiklikte kopya" pahalı değil mi?
  5. Nerede en çok işe yarar?
  6. Sonuç

Değişmezlik, bir nesnenin oluşturulduktan sonra durumunun sabit kalmasıdır. İlk duyulduğunda savurgan görünür: her güncelleme için yeni nesne mi üreteceğiz? Ama pratikte değişmezlik bir bellek tercihinden çok, tüm bir hata sınıfını ortadan kaldıran bir tasarım kararıdır.

Değişmezliğin engellediği hata türleri listesi
Değişmezlik bir performans tekniği değil, bir hata sınıfını yok etme tekniğidir.

Mutasyonun sessiz maliyeti

Aşağıdaki kod hatalı görünmüyor, ama gerçek projelerde en çok zaman kaybettiren hata türlerinden birini barındırıyor.

PHP
class Sepet
{
    public function __construct(public array $urunler = []) {}
}

$sepet   = new Sepet(['kalem', 'defter']);
$yedek   = $sepet;              // Nesne referansı kopyalandı, nesne DEĞİL
$yedek->urunler[] = 'silgi';

print_r($sepet->urunler);       // ['kalem', 'defter', 'silgi'] — beklenmedik!

Burada "yedek" aldığını sanan geliştirici aslında aynı nesneye ikinci bir isim vermiştir. Bu hata küçük bir dosyada hemen görülür; on ekranlık bir servis katmanında haftalarca fark edilmez. Bellek yönetimi modelleri yazısında değindiğimiz "sahiplik" kavramı tam da bu belirsizliği çözmeye çalışır.

PHP 8.1+ ile değişmez nesneler

PHP 8.1 ile gelen readonly özelliği, alanların kurucudan sonra yazılmasını dil düzeyinde engeller. Güncelleme gerektiğinde "üzerine yaz" yerine "yenisini üret" yaklaşımı kullanılır.

PHP
final class Para
{
    public function __construct(
        public readonly int $kurus,
        public readonly string $birim = 'TRY',
    ) {
        if ($kurus < 0) {
            throw new InvalidArgumentException('Negatif tutar olamaz');
        }
    }

    public function ekle(Para $diger): static
    {
        if ($diger->birim !== $this->birim) {
            throw new InvalidArgumentException('Birimler farklı');
        }

        return new static($this->kurus + $diger->kurus, $this->birim);
    }

    public function __toString(): string
    {
        return number_format($this->kurus / 100, 2, ',', '.') . ' ' . $this->birim;
    }
}

$a = new Para(1250);
$b = $a->ekle(new Para(750));

echo $a;  // 12,50 TRY  — değişmedi
echo $b;  // 20,00 TRY  — yeni nesne

Bu tasarımın asıl kazancı şudur: $a değişkenini bir fonksiyona verdiğinizde, o fonksiyonun onu bozamayacağını bilirsiniz. Doğrulama kurucuda bir kez yapılır ve nesne ömrü boyunca geçerli kalır. Negatif tutar kontrolünü her kullanım yerinde tekrarlamak zorunda kalmazsınız.

JavaScript tarafı: const yeterli değil

JavaScript'te sık yapılan yanlış, const'un bu güvenceyi sağladığını sanmaktır. const yalnızca bağlamayı dondurur, nesnenin içeriğini korumaz.

JavaScript
const ayarlar = { tema: 'koyu', dil: 'tr' };
ayarlar.tema = 'acik';        // Serbest! const engellemez

// Gerçekten dondurmak için:
const sabit = Object.freeze({ tema: 'koyu', dil: 'tr' });
sabit.tema = 'acik';          // Sessizce yok sayılır (strict mode'da hata)

// Üzerine yazmak yerine yeni nesne üretmek:
const yeni = { ...sabit, tema: 'acik' };

Object.freeze yalnızca bir seviye dondurur. İç içe nesneler hâlâ yazılabilir; derin dondurma gerekiyorsa özyinelemeli bir yardımcı yazmanız ya da yapısal paylaşım kütüphanesi kullanmanız gerekir.

"Her değişiklikte kopya" pahalı değil mi?

Küçük nesneler için maliyet ihmal edilebilir. Büyük koleksiyonlar içinse yapısal paylaşım (structural sharing) devreye girer: yeni sürüm, değişmeyen alt ağaçları eski sürümle paylaşır, yalnızca dokunulan dalı kopyalar. React ekosistemindeki Immer, Java'daki persistent collection kütüphaneleri bu ilkeyle çalışır.

Buna karşılık gerçekten sıcak döngülerde — örneğin milyonlarca satırlık bir veri dönüşümünde — kopyalama ölçülebilir bir maliyettir. Kural şudur: varsayılan sabit olsun, ölçüm aksini söylediğinde yerel olarak esnek yapıya inin. Erken iyileştirme yapmak yerine önce ölçmek, performans çalışmalarında da aynı şekilde geçerli bir kuraldır.

Kullanıcı girdisiyle çalışırken de aynı ilke geçerlidir: doğrulanmış veriyi değişmez bir nesneye almak, istemci ve sunucu doğrulaması arasındaki tutarsızlıkları görünür kılar.

Nerede en çok işe yarar?

Değişmezliğin getirisi en yüksek olduğu üç yer: değer nesneleri (para, tarih aralığı, koordinat), yapılandırma (uygulama ayarları, bir kez okunur çok kez kullanılır) ve eşzamanlı erişilen veriler (kuyruk mesajları, önbellek girdileri). Bu üç yerde değişmezlik, yarış durumlarını ve "kim değiştirdi?" araştırmalarını tamamen ortadan kaldırır. Dilinizin sunduğu araçlar için PHP resmî belgeleri güncel referanstır.

Sonuç

Değişmezlik, bellekten tasarruf etmek için değil, bir hata sınıfını kökten yok etmek için tercih edilir: beklenmedik mutasyon, sığ kopya yanılgısı ve yarış durumu. PHP tarafında readonly, JavaScript tarafında Object.freeze ve yayma (spread) operatörü bu yaklaşımı dil desteğiyle mümkün kılar. Uygulama stratejisi nettir: yeni yazdığınız değer nesnelerini varsayılan olarak değişmez tasarlayın, mevcut kodda ise en çok hata çıkaran sınıftan başlayın. Bir sonraki hata avında "bu değere kim dokundu?" sorusunu sorduğunuzda, cevabın "kimse dokunamaz" olması ne kadar zaman kazandırdığını göreceksiniz.

Beğeniniz benzer içerikleri öne çıkarmamıza yardımcı olur.

Sık sorulan sorular

readonly ile private set arasındaki fark nedir?

readonly, alanın sınıf içinden bile kurucudan sonra yazılmasını engeller; private set ise yalnızca dışarıdan yazmayı kapatır, sınıf içi metotlar hâlâ değiştirebilir. Gerçek değişmezlik isteniyorsa readonly doğru araçtır.

Değişmez nesnelerle ORM nasıl çalışır?

ORM katmanları genellikle nesneyi veritabanından doldururken alanlara yazar, bu da readonly ile çakışabilir. Yaygın çözüm, kalıcılık modeli ile alan modelini ayırmaktır: ORM entity değişebilir kalır, iş mantığında kullanılan değer nesneleri değişmez olur.

Object.freeze performansı düşürür mü?

Modern JavaScript motorlarında dondurulmuş nesnelere erişim genelde aynı hızdadır; asıl maliyet dondurma işleminin kendisidir. Bu yüzden freeze işlemini sıcak döngü içinde değil, nesne oluşturulurken bir kez yapmak gerekir.

Değişmezlik test yazmayı nasıl etkiler?

Belirgin biçimde kolaylaştırır. Girdi nesnesinin test sırasında değişmediğini bildiğiniz için testler arası sızıntı olmaz, aynı girdiyle aynı çıktıyı garanti edersiniz ve kırılgan testlerin en yaygın sebeplerinden biri olan paylaşılan durum ortadan kalkar.

Yazan

Çılgın Yazılım

Ben Evren Çılgın; yazılım geliştirme, web teknolojileri ve sistem tasarımı alanlarında uzmanlaşmış bir geliştiriciyim. Uzun yıllardır hem frontend hem de backend tarafında üretken, ölçeklenebilir ve kullanıcı dostu çözümler üretiyorum.

Hakkımızda Bize ulaşın

Bu konuda bir projeniz mi var?

İhtiyacınızı birkaç cümleyle anlatın; uygun yaklaşımı birlikte belirleyelim.

Projenizi Anlatın

İlgili yazılar

Yorumlar

Henüz yorum yok. Sorunuzu ya da deneyiminizi ilk siz yazın.

Yorum yazın

Yorumunuz onaylandıktan sonra yayınlanır. Ekibimiz gerekirse konuyla ilgili bir yanıt da paylaşır.

Yayınlanmaz; yalnızca gerektiğinde size ulaşmak için.

En az 10 karakter.

Tüm yazılar