Yegane Tasarım Kalıbı (Singleton Design Pattern)

Eyl 08, 2013

Nesne temelli yazılım mimarilerinde belirlenmiş bazı temel tasarım kalıpları vardır. Bunların en önemlilerinden biri de "Singleton" tasarım kalıbıdır. Bu kalıp, bir sistem içerisinde yalnızca bir nesne kullanılmak istendiğinde çok yararlı olabilmektedir.

Tasarım Kalıbının Gerçeklenmesi

Singleton tasarım kalıbı bir programın yaşam sürecinde belirli bir nesneden sadece bir örneğin olmasını garanti altına alır. Eğer nesne yaratılmışsa, bu kalıp (pattern) sayesinde yeni bir yaratma işlemi yerine bu nesnenin referansı işaret edilir. Aynı zamanda bu kalıp, yaratılan tek nesneye, ilgili sınıfın dışından global düzeyde erişilmesini hedefler. Singleton kalıbında istenilen yalnızca bir nesnenin yaratılması olduğuna göre ilgili sınıfın içinde bir yerde nesnenin oluşturulması gerekir. Bu da statik bir özellik (property) ya da statik bir metot ile mümkündür. Bu statik metot, sınıfın kendi içinde yaratılan nesneyi geri dönüş değeri olarak dönecektir. Bu nesne statik metodun ya da özelliğin içinde yaratılıp yine sınıfın private (gizli) olan elemanına atanır. Tekil olarak yaratılan bu nesneye erişmek için bu private olan elemanın referasına geri dönmek yeterli olacaktır.

Örneğin; bir muhasebe sisteminde ay sonu hesap kesimini yapan sınıf, kredi kartı ile ödeme yapılan bir sistemdeki kredi kartı para çekme işlemini yapan sınıf, uçak iniş kalkışlarının ayarlandığı uçuş yönetimini sağlayan sınıf sistemde sadece bir tane olması gereken sınıflardır. Singleton kalıbı ile bu koşul garanti altına alınmış olur.

Sınıf Diyagramı

Singleton'ın statik yapısını gösteren UML diyagramının basit olmasının sebebi bu tasarım kalıbının kendisinden yaratılmış tek olan nesnenin (instance) referansını tutan basit bir sınıfı içermesidir.

Statik Metodun Uygulaması

Singleton Kalıbının Örnek Kodları

  • Singleton Kalıbının 1. Versiyonu :

    public class SingletonTasarımKalıbı
        {
            private static SingletonTasarımKalıbı nesne = new SingletonTasarımKalıbı();
       
            private SingletonTasarımKalıbı()
            {
            }
            public static SingletonTasarımKalıbı Nesne
            {
                get
                {
                    return nesne;
                }
            }
        }
       
       
  • Singleton Kalıbının 2. Versiyonu :

    public class SingletonTasarımKalıbı
        {
             private static SingletonTasarımKalıbı nesne = new SingletonTasarımKalıbı(); 
        
             private SingletonTasarımKalıbı()
             {
             }
             public static Singleton Nesne()
             {
                  return nesne;
             }
        }

  • İki sınıfın tek farkı oluşturulan nesneye erişme biçimidir. İlk versiyonda nesneye özellik (property) üzerinden erişilirken ikinci versiyonda metot üzerinden erişilmektedir. Ancak her iki erişim aracı da statik tanımlanmaktadır.

    Yukarıdaki her iki versiyonda da yaratılan nesne

    SingletonTasarımKalıbı nesne = SingletonTasarımKalıbı.Nesne;

    ya da;

    SingletonTasarımKalıbı nesne = SingletonTasarımKalıbı.Nesne();

    şeklinde tanımlandığında nesne zaten yaratılmış durumda olmaktadır. Bu sınıfı daha dinamik bir hale getirmek için yaratılacak nesnenin ancak istenildiği zaman yaratılması sağlanabilir. 3. versiyonda bu duruma ait bir örnek görülmektedir.

  • Singleton Kalıbının 3. Versiyonu :

    public class Singleton
         {
            private static SingletonTasarımKalıbı nesne;
         
            private SingletonTasarımKalıbı() 
            { 
            }
            public static Singleton Nesne()
            {
               if(nesne == null)
                  nesne = new SingletonTasarımKalıbı();
         
               return nesne;
            }
         }

  • Nesne, sınıf belleğe yüklendiği sırada değil, o nesne ilk defa kullanılmak istediği sırada yaratılmaktadır. İlgili nesneyi her kullanmak istediğimizde yeni bir nesnenin yaratılmaması için ise, if(nesne == null) şeklinde bir koşul kullanılmıştır.

    Eğer çok kanallı (multi-thread) bir uygulama geliştirilmiyorsa, ilk 3 versiyondan biri kullanılabilir. Çok kanallı bir uygulamada ise farklı kanalların aynı nesneden tekrar yaratmasını engellemek için başka kontrollerin de yapılması gerekmektedir. Bu kontroller yazılan kodun performansını düşürmektedir.

  • Singleton Kalıbının 4. Versiyonu :

    public class SingletonTasarımKalıbı
          {
            private static SingletonTasarımKalıbı nesne;
         
            private static Object kanalKontrol = new Object;
         
            private SingletonTasarımKalıbı()
            {
            }
            public static Singleton Nesne()
            {
               if(nesne==null)
               {
                  lock(kanalKontrol)
                  {
                     if(nesne==null)
                     {
                        nesne = new SingletonTasarımKalıbı();
                     }
                  }
               }
               return nesne;
            }
         }

  • Eğer nesne ilk defa yaratılacaksa (önceden null değere sahipse) lock anahtar sözcüğü ile işaretlenen blok kilitlenerek başka kanalların bu bloğa erişimi engellenmiş olur. Kilitleme işlemi bittiğinde nesne yaratılmış olacağından kilidin kalkmasını bekleyen diğer kanal lock bloğuna girdiğinde, bu bloktaki ikinci if konrtolü nesnenin yeniden yaratılmasını engelleyecektir.

  • Singleton Kalıbının 5. Versiyonu :

    public class SingletonTasarımKalıbı
           {
              private static SingletonTasarımKalıbı nesne = new SingletonTasarımKalıbı ();
         
            private static SingletonTasarımKalıbı()
            {
            }
            private SingletonTasarımKalıbı
            {
            }
            public static SingletonTasarımKalıbı Nesne
            {
               get
               {
                  return nesne;
               }
            }
         }

  • 5. versiyonun 1. versiyondan farkı, yapıcı metodun statik tanımlanmasıdır. C# dilinde statik yapıcı metotlar; ancak bir nesne yaratıldığında ya da statik bir eleman referans edildiğinde sadece bir kez çalıştırılır. Böylece, 5. versiyonda farklı kanalların birden fazla nesne yaratması mümkün değildir.

    Singleton Kalıbının Faydaları ve Sakıncaları

    Faydaları :

    1. Nesne Kontrolü : Diğer nesnelerin Singleton nesnesinden kendi kopyalarını yaratmalarını engelleyerek tüm nesnelerin yalnızca bir Singleton nesnesine ulaştığından emin olunmasını sağlar.
    2. Esneklik : Sınıf yaratım sürecini kontrol etme ve değiştirme özelliğine sahiptir. Statik sınıf ile bunu gerçekleştirmek mümkün değildir.

    Sakıncaları:

    1. Ek Yük : Diğer nesneler her referans istediğinde Singleton nesnesi önceden yaratılmış mı kontrolü yapmak ek bir yük getirir. Statik yaratım ile bu sorunun üstesinden gelinebilir.
    2. Olası Geliştirme Karmaşası : Uygulama geliştirme sırasında Singleton nesnesi yaratılırken “new” kelimesi kullanılamaz. Çünkü uygulama geliştiriciler bazen kaynak koda erişemeyebilirler ve sınıftan direk olarak bir nesne yaratamayabilirler.
    3. Nesne Ömrü : Singleton, nesnenin nasıl silineceğini söylemez. Bellek yönetimi sağlayan dillerde sadece Singleton kalıbı nesneyi serbest bırakabilir (deallocate), çünkü sınıf nesneye private bir referans tutar. C++ gibi dillerde ise, diğer sınıflar nesneyi silebilir, fakat bu yöntem Singleton sınıfı içerisinde boşta kalan referansa neden olur.