Effective Java Madde 3: Singleton Sınıfları Private Yapıcı Metot veya Enum Türüyle Güçlendirin

Singleton en basit anlamıyla sadece bir kez somutlaştırılabilen (instantiate) sınıf anlamına gelir. Diğer bir değişle, singleton sınıflardan sadece bir kez nesne oluşturulabilir. Bu nesneler ya fonksiyon gibi durum içermeyen nesneleri, (Madde 24) ya da doğası itibariyle eşsiz olan bileşenleri temsil ederler. Bir sınıfı singleton yapmak, onu kullanan istemcileri test etmeyi zorlaştırır çünkü singleton nesneyi bir mock gerçekleştirim ile değiştirmek eğer bu nesne bir arayüzü gerçekleştirmiyorsa imkansızdır.

Singleton nesne oluşturmak için iki yöntem vardır. Her ikisi de yapıcı metotları private tutarak, singleton nesneyi public static bir üye olarak dışarı açma esasına dayanmaktadır. Bu yöntemlerden ilkinde, singleton nesne final olarak tanımlanmaktadır:

// public final olarak tanımlanmış singleton alan
public class Elvis {

public static final Elvis INSTANCE = new Elvis();
private Elvis() { ... }
public void leaveTheBuilding() { ... }
}

Burada private yapıcı metot sadece bir kez çalıştırılmakta ve public static final olarak tanımlanmış Elvis.INSTANCE alanını oluşturmaktadır. Burada public veya protected bir yapıcı metot bulunmaması Elvis sınıfından sadece bir tane nesne oluşturulabilmesini garanti eder. İstemci bir sınıfın bu durumu değiştirebilmesi için yapabileceği tek şey AccessibleObject.setAccessible metodu yardımıyla reflection mekanizmasını kullanarak private yapıcı metodu çalıştırmaktır. (Madde 65) Buna karşı tedbir almak için yapılabilecek şey, private yapıcı metodu değiştirerek ikinci bir nesne yaratılmaya çalışıldığında exception (istisna) fırlatmaktır. Singleton nesne yaratmak için kullanılan ikinci yöntemde, public static bir fabrika metodu kullanılmaktadır:

// Static fabrika ile Singleton
public class Elvis {

    private static final Elvis INSTANCE = new Elvis(); 
    private Elvis() { ... }

    public static Elvis getInstance() { 
        return INSTANCE; 
    }

    public void leaveTheBuilding() { ... }
}

Elvis.getInstance() her çağrıldığında aynı nesne referansını döndürecektir ve ikinci bir nesne yaratılmayacaktır. (daha önce belirttiğimiz reflection yöntemi hariç) public alan kullanan birinci yöntemin avantajı basit olmasıdır. Bunun yanında birinci yöntemde API sınıfın singleton olduğunu açıkça göstermektedir. Kullanılan public alan final olduğu için her seferinde aynı nesne referansını döndürecektir. Static fabrika kullanan ikinci yöntemin avantajı ise API değiştirmeden nesnenin singleton özelliğini değiştirme esnekliğini vermesidir. Fabrika metodu sürekli aynı nesneyi döndürmektedir, ancak bunu değiştirerek istemcilere hiç dokunmadan örneğin çağrı yapan her bir thread için yeni bir nesne oluşturacak şekilde değiştirebiliriz. İkinci bir avantaj olarak, istenildiği taktirde generic bir singleton fabrika metodu yazılabilir. (Madde 30) Bu yöntemin son bir avantajı ise metot referansı bir Supplier olarak kullanılabilir. Örneğin Elvis::getInstance, Supplier<Elvis> olarak kullanılabilir. Bu avantajlardan birisi uygulamanıza faydalı olacaksa ikinci yöntem, değilse birinci yöntem tercih edilebilir. Bu iki yöntemden birini kullanan bir singleton sınıfı serializable yapmak için implements Serializable eklemek yeterli olmayacaktır. Sınıfın singleton özelliğini korumak için, sınıfın bütün alanlarını transient olarak tanımlamak ve readResolve metodunu gerçekleştirmek gerekir. (Madde 89) Aksi taktirde, serileştirilen nesne yeniden okunduğunda (deserialization), singleton nesneyi döndürmek yerine yeni bir nesne yaratılacaktır. Bunu engellemek için aşağıdaki gibi bir readResolve metodu yazılabilir

// Singleton alanı korumak için gereken readResolve metodu
private Object readResolve() {
    // yeni bir nesne yaratmak yerine var olan Elvis nesnesini döndür
   return INSTANCE;
}

Singleton nesne yaratmanın üçüncü bir yolu ise tek elemanlı bir Enum kullanmaktır.

// Enum ile gerçekleştirilen singleton
public enum Elvis {
    INSTANCE;

    public void leaveTheBuilding() { ... }
}

Bu yaklaşım daha önce bahsedilen, public static alan kullanan birinci yönteme çok benzemekle birlikte ondan daha kısa ve özdür. Yukarıda bahsedilen serileştirme mekanizmasını otomatik olarak sağlar. Birden fazla nesne yaratmaya karşı da çok güçlü bir koruma sağlar, yazıda daha önce bahsedilen reflection yöntemi kullanarak bile ikinci bir nesne yaratılamaz. Her ne kadar tuhaf görünse de tek elemanlı Enum yöntemi çoğu zaman singleton nesne yaratmak için kullanılabilecek en iyi yöntemdir. Ancak bu yöntemi Enum’dan başka bir sınıfı kalıtmak istiyorsanız kullanamazsınız. Her ne kadar kalıtım kısıtı olsa da, Enum türleri arayüzleri gerçekleştirmekte özgürdür.

Share

Bir Cevap Yazın