January 28, 2026 (3mo ago)

Temiz Kod için Java Singleton Deseni Rehberi

İş parçacığı güvenliği, test etme ve bağımlılık enjeksiyonu gibi modern alternatiflerle sürdürülebilir, ölçeklenebilir kod için Java singleton desenini ustalıkla öğrenin.

← Back to blog
Cover Image for Temiz Kod için Java Singleton Deseni Rehberi

İş parçacığı güvenliği, test etme ve bağımlılık enjeksiyonu gibi modern alternatiflerle sürdürülebilir, ölçeklenebilir kod için Java singleton desenini ustalıkla öğrenin.

Temiz Kod için Java Singleton Deseni Rehberi

Özet: İş parçacığı güvenliği, test edilebilirlik ve bağımlılık enjeksiyonu gibi modern alternatiflerle sürdürülebilir, ölçeklenebilir kod için Java singleton desenini ustalıkla öğrenin.

Giriş

Java'daki Singleton Deseni (Singleton Pattern) bir sınıfın yalnızca tek bir örneğe sahip olmasını zorunlu kılar ve ona global, tek bir erişim noktası sağlar. Bu, birden fazla örneğin tutarsız duruma veya kaynak israfına neden olacağı yapılandırma yöneticileri, bağlantı havuzları veya merkezi günlükleme servisleri gibi nesneler için kullanışlıdır. Güvenli, test edilebilir bir Singleton'ı nasıl uygulayacağınızı — ve ne zaman kaçınmanız gerektiğini — anlamak, temiz ve sürdürülebilir Java kodu için esastır.

Singleton Tasarım Desenini Anlamak

Renkli uçakların dairesel bir düzen içinde etrafını sardığı bir hava trafik kontrol kulesi, hava trafik yönetimini ilustr eder.

Yararlı bir benzetme, bir havalimanının hava trafik kontrol kulesidir. Her uçak için ayrı bir kule inşa etmezsiniz; her uçak aynı kule ile iletişim kurar. Kule tek doğru kaynaktır. Singleton'ın uygulamadaki rolü budur.

Bu desen, klasik tasarım desenleri literatüründe ve kurumsal Java sistemlerinde popüler hale gelmiştir1. Temel sorumlulukları basittir:

  • Tek bir örneği garanti etmek — genellikle kurucuyu private yaparak.
  • Global bir erişim noktası sağlamak — genellikle getInstance() gibi statik bir yöntem.

Ana Özellikler

  • Sadece bir örnek: Sınıf birden fazla örnek oluşturulmasını engeller.
  • Private kurucu: Diğer sınıfların doğrudan örnek oluşturmasını engeller.
  • Global erişim noktası: Statik bir yöntem tek örneği döner.
  • Kendi yaşam döngüsünü yönetme: Sınıf kendi örneğini yönetir.

Önemli çıkarım: Singleton, tek bir nesneyi ve kontrollü global erişimi zorunlu kılar, böylece uygulamanın farklı bölümleri tam olarak aynı örnekle konuşur.

Java'da İş Parçacığı Güvenli Singleon'lar Uygulama

Çoklu renkli oklar alan ve bir asma kilitli kasa illüstrasyonu, güvenli depolamayı simgeler.

Naif bir tembel başlatmalı (lazy-initialized) Singleton basittir ama iş parçacığı güvenli değildir. Aşağıdaki temel örneği düşünün:

public class BasicLazySingleton {
    private static BasicLazySingleton instance;

    private BasicLazySingleton() {}

    public static BasicLazySingleton getInstance() {
        if (instance == null) {
            instance = new BasicLazySingleton();
        }
        return instance;
    }
}

Çok iş parçacıklı bir ortamda, iki iş parçacığı instance == null durumunu görüp her ikisi de yeni bir örnek oluşturabilir. Basit bir düzeltme, getInstance()'ı synchronized yapmak olsa da bu her çağrıda gereksiz kilitlemeye neden olur.

Synchronized Yöntem (çalışır, ama maliyetli)

public class SynchronizedSingleton {
    private static SynchronizedSingleton instance;

    private SynchronizedSingleton() {}

    public static synchronized SynchronizedSingleton getInstance() {
        if (instance == null) {
            instance = new SynchronizedSingleton();
        }
        return instance;
    }
}

Bu, iş parçacığı güvenliğini sağlar ancak performansa zarar verir çünkü senkronizasyon her erişimde çalışır.

Bill Pugh (Initialization-on-demand Holder)

Daha temiz bir yaklaşım Initialization-on-demand Holder deyimidir. Bu, JVM'de sınıf başlatmanın iş parçacığı güvenli olması nedeniyle senkronizasyon yükü olmadan tembel başlatma ve iş parçacığı güvenliği sağlar2.

public class BillPughSingleton {
    private BillPughSingleton() {}

    private static class SingletonHolder {
        private static final BillPughSingleton INSTANCE = new BillPughSingleton();
    }

    public static BillPughSingleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

Bu yöntem, SingletonHolder'ın yalnızca getInstance() çağrıldığında yüklenmesine dayanır ve sınıf başlatma garantileri iş parçacığı güvenliğini sağlar.

Enum Singleton'lar (önerilen)

Joshua Bloch, bir Singleton için tek elemanlı enum kullanılmasını önerir. Bu kısa ve yansıtma (reflection) ile serileştirme saldırılarına karşı koruma sağlar3.

public enum EnumSingleton {
    INSTANCE;

    public void someMethod() {
        // business logic
    }
}

Faydaları:

  • Minimal kod
  • JVM tarafından sağlanan iş parçacığı güvenliği
  • Serileştirme güvenliği
  • Yansıtma tabanlı örnek oluşturma girişimlerine karşı güçlü koruma

Çoğu durum için enum Singleton en sağlam ve sürdürülebilir seçimdir.

Singleton Deseninin Gizli Maliyetleri

Duvarlarda elektronik cihazların olduğu, renkli kablolarla birçok böceğe bağlanmış bir odanın sürreal bir eskizi.

Singleton'lar belirli bir problemi çözerken gizli maliyetler getirir: sıkı bağlılık (tight coupling), global durum ve azalan test edilebilirlik.

Kod Singleton.getInstance() çağırdığında, o bağımlılık gizlenir. Sınıfın herkese açık sözleşmesi (public contract) onun bir global nesneye dayandığını açığa çıkarmaz. Bu şunlara yol açar:

  • Değiştirmesi zor, katı kod
  • Gerçek global örneğe ihtiyaç duyan kırılgan testler
  • Paylaşılan durum nedeniyle paralel test çalıştırma zorluğu

Test Etme Problemleri

Singleton'lar izole birim testi yapmayı zorlaştırır. Bir mock'u kolayca yerine koyamazsınız, bu yüzden testler genellikle gerçek uygulamayı kullanır. Bu, yavaş testlere, istemeden dış sistemlere bağlılığa ve kırılgan CI boru hatlarına neden olabilir.

Bir sınıfın Singleton'a bağımlı olması, bu bağımlılığı imzasından (signature) gizler; bu da kodun anlaşılmasını ve bakımını zorlaştırır.

Global Durum ve Gizli Bağımlılıklar

Singleton özünde bir global değişkendir. Global durum bilgi akışını gizler ve çözülmesi zor karşılıklı bağımlılıklar yaratır. Bu, hata ayıklamayı (debugging) karmaşıklaştırır ve geliştirmeyi yavaşlatır.

Yaygın anti-deseni ve sürdürülebilirlik konularında daha fazla bilgi için OOP'deki tasarım desenleri ve test stratejileri rehberimize bakın.

Singleton'lara Modern Alternatifler

Sistemler geliştikçe, geliştiriciler Singleton'ın sakıncalarından kaçınan, kontrollü nesne oluşturmayı koruyan desenleri benimsediler.

Bağımlılık Enjeksiyonu

Bağımlılık Enjeksiyonu (Dependency Injection, DI) sorumluluğu tersine çevirir: istemciler bağımlılıklarını beyan eder ve harici bir konteyner onlarla ilgilenir. Bu, bağımlılıkları açık hale getirir ve testlerde kolayca değiştirilebilir kılar. Spring ve Guice gibi DI çerçeveleri nesne yaşam döngülerini ve bağlantılarını sizin için yönetir4.

DI'nin faydaları:

  • Gevşek bağlılık — bileşenler somut sınıflara değil soyutlamalara bağlıdır
  • Test edilebilirlik — mock veya fake enjeksiyonu yapabilirsiniz
  • Esneklik — yapılandırma ile uygulamaları değiştirebilirsiniz

Bu, kontrolün tersine çevrilmesi (inversion of control) en iyi uygulamalarıyla uyumludur ve daha net, sürdürülebilir sistemler üretir7.

Fabrikalar

Factory deseni oluşturma mantığını merkezi hale getirir. Bir fabrika gerektiğinde aynı örneği veya yeni örnekleri döndürebilir. İstemci kodu, bir nesnenin nasıl oluşturulduğunu bilmeden fabrika aracılığıyla nesne ister; bu da kodunuzu modüler ve test edilebilir tutar.

Scoped (Kapsamlı) Örnekler

Bazen tek bir örneğe ihtiyaç duyarsınız, ancak yalnızca sınırlı bir kapsam içinde (istek, oturum veya uygulama). Çerçeveler, istek kapsamlı veya oturum kapsamlı bean'ler sağlayarak kaynak yeniden kullanım ile izolasyon arasında bir denge sağlar.

Dağıtık Sistemlerde Singleon'lar

Bir JVM-yerel Singleton, birden fazla hizmet örneği arasında tek bir örnek sağlamaz. Mikroservisler birden fazla JVM çalıştırır, bu yüzden paylaşılan durum için Redis veya Consul ya da Spring Cloud Config gibi merkezi yapılandırma servisleri gibi dağıtık çözümlere ihtiyacınız olur6.

Legacy Koddan Singleton'ları Refaktör Etme

Bir adam, konteyner için karmaşık, ağır bir monoliti birçok küçük, renkli modüle ayırıyor.

Singleton'ları refaktör etmek dikkat gerektirir. Temel sorun, statik getInstance() çağrısına doğrudan bağımlılıktır. Kademeli, metodik bir yaklaşım riski azaltır.

Adım adım strateji:

  1. Singleton'ın herkese açık yöntemlerini tanımlayan bir arayüz (interface) ekleyin ve Singleton'ın bu arayüzü uygulamasını sağlayın.
  2. Singleton kullanan sınıflarda bağımlılıkları açık hale getirmek için kurucu parametreleri ekleyin.
  3. Bir fabrika veya DI konteyneri (Spring, Guice) kullanarak uygulamayı yönetilen bir örnek olarak sağlayın.
  4. Singleton.getInstance() çağrılarını kurucu enjeksiyonuyla sağlanan bağımlılıklarla değiştirin.
  5. Tüm çağıranlar bağımlılığı harici olarak aldıktan sonra Singleton'a özgü kodu kaldırın.

Faydalar: geliştirilmiş modülerlik, test edilebilirlik ve netlik. Singleton'ları refaktör etmek, katı bir kod tabanını esnek, test edilebilir bileşenlere dönüştürür.

Sıkça Sorulan Sorular

Singleton deseni bir anti-pattern midir?

Çoğu zaman, evet. Singleton global durum ve sıkı bağlılık getirir, bu da test edilebilirliği azaltır ve uzun vadeli bakım maliyetini artırır. Bunu sınırlı kullanın ve mümkünse DI veya kapsamlı örnekleri tercih edin.

Java'da bir Singleton nasıl kırılabilir?

Yansıtma (reflection) ve serileştirme yeni örnekler oluşturabilir, bunlara karşı açıkça korunmadığınız sürece. Bir singleton için enum kullanmak bu tuzaklardan kaçınır3.

Mikroservisler için Singleton uygun mudur?

Hayır. Singleton JVM'e özgüdür, bu yüzden her hizmet örneği kendi Singleton'ını alır. Hizmetler arası paylaşılan durum için Redis veya merkezi yapılandırma servisleri gibi dağıtık sistemleri kullanın6.

Üç kısa Soru-Cevap özeti

S: Ne zaman Singleton kullanmalıyım? C: Yalnızca tek bir JVM içinde gerçekten tek, global bir kaynağı temsil eden bir örnek gerektiğinde ve daha iyi bir alternatif yoksa. Zorunluluk halinde enum Singleton'ları tercih edin.

S: İş parçacığı güvenli, tembel bir Singleton nasıl yapılır? C: Initialization-on-demand Holder deyimini (Bill Pugh) veya bir enum kullanın. Her ikisi de düşük maliyetle iş parçacığı güvenliği sağlar; enum ayrıca serileştirme ve yansıtma sorunlarına karşı korur23.

S: Daha iyi test edilebilirlik için Singleton yerine ne kullanmalıyım? C: Bağımlılık Enjeksiyonu, fabrikalar veya kapsamlı örnekler kullanın; böylece bağımlılıklar açık olur ve testlerde kolayca değiştirilebilir47.


1.
Design Patterns: Elements of Reusable Object-Oriented Software (the “Gang of Four”), Erich Gamma et al., 1994. https://en.wikipedia.org/wiki/Design_Patterns
2.
Java Language Specification, section on initialization of classes and interfaces; class initialization is performed at first active use and is thread-safe. https://docs.oracle.com/javase/specs/
3.
Joshua Bloch, Effective Java — recommends enum singletons for serialization and reflection safety. https://www.pearson.com/en-us/subject-catalog/p/effective-java/P200000006973/9780134685991
4.
Spring Framework documentation on Dependency Injection and bean scopes. https://spring.io/projects/spring-framework
5.
Serialization and reflection pitfalls are discussed in Effective Java and Java serialization documentation. https://docs.oracle.com/javase/8/docs/platform/serialization/spec/serial-arch.html
6.
Microservices patterns and guidance on distributed state; single-JVM singletons don’t provide cross-service singleton semantics. See Microservices.io and related resources. https://microservices.io/
7.
Martin Fowler on Inversion of Control and Dependency Injection principles. https://martinfowler.com/articles/injection.html
← Back to blog
🙋🏻‍♂️

AI kod yazar.
Siz onu uzun süre dayanır hale getirirsiniz.

AI hızlanması çağında, temiz kod sadece iyi bir uygulama değil — ölçeklenen sistemlerle kendi ağırlığı altında çöken kod tabanları arasındaki farktır.