Singleton Tasarım Deseni

header

Yaratımsal desenler içerisinde yer alan singleton bir sınıfın uygulama yaşam süresi boyunca tek bir nesne örneğinin oluşturulmasını sağlamak için kullanılır.Ayrıca singleton olarak tasarlanan sınıfın global bir erişim seviyesine sahip olmasını da garanti eder.Bir sınıfın örneğinin oluşturulması istediğimizde newanahtar kelimesini kullanırız.Bu anahtar kelimeyi kullandığımız sınıflara bakıldığında yapıcı methodun erişim belirleyicisinin public olduğunu görürsünüz.Yeni yazılan sınıfların yapıcı methodu varsayılan olarak gizli bir puclic yapıcı methoduna sahiptir böylelikle new anhatar kelimesiyle nesneyi oluşturabiliyoruz.Singleton olarak tasarladığımız sınıflar da bu yapıcı method dışarıdan erişime kapatılarak sınıftan nesnelerin oluşturulması engellenmiş olur.Bir sınıfı singleton olarak tasarlamak istediğimiz de aşağıdaki bir kaç adım bize yardımcı olacaktır.

  • ilk olarak sınıftan nesne oluşturulmasını sağlayan yapıcı methodun erişim belirleyicisi private veya protected yapılarak dışardan erişime kısıtlanır.Bunu yaparak sınıfın new  ile oluşturulması engellemiş oluyoruz
  • Sınıfı sealed olarak işaretleyebiliriz
  • Nesne yaratmak için sınf içerisinde kendi tipinde private erişime sahip static bir değişken tanımlanır.
  • Son olarak singeleton tipimizi temsil etmek için static ve public olarak bir method veya propertytanımlıyoruz.Bunun içerisinde nesnenin yaratılması sağlanıcak.Bu method veya property yi kullanarak istemci nesneye erişim sağlar.

singleton-design-pattern

İstemci kod parçası singleton olan sınıfın örneğinin oluşturmak istediğinde eğer daha önce hiç nesne oluşmadıysa ilk olarak nesne oluşur fakat daha sonraki nesne çağrımlarında daha önce oluturulan nesne gönderilir.Yukarıdaki resimde geleneksel ve singleton implementasyonunu görebilirsiniz.

Uygulanabilirlik

Singleton deseni aşağıdaki durumlarda kullanılabilir.

  • Bir sınıftan sadece bir nesne örneğini,n olması öngörüldüğünde
  • Singleton uygulanacak sınıfın global bir erişim seviyesine sahip olması durumunda ve uygulamanın diğer tüm bileşenleri bu sınıfa erişilebilmeli
  • Sınıftan nesne oluşturması masraflı olduğu durumlar da 
  • Singleton olması istenen sınıfın yapıcı methodu parametresiz olması gerekir

Singleton deseni kullanılma sıklığı diğer desenlere göre daha fazladır ve çok daha kolay uygulanır.Bir framework tasarımında genelde caching , logging , kaynak tüketim sınıfları (database connections,file processor) gibi sınıflar singleton olarak tasarlanır.

Tasarım

Desenin UML şemasını görelim.

singleton_diagram

Singleton desenenin single-threaded ve multi-threaded olarak iki farklı gerçekleştirim yönetemi vardır.Kodunuzun bulunduğu ortamın multi-thread olup olmamasına bağlı olarak bu yöntemler kullanılır.Aşağıda farklı singleton uygulama versiyonlarını görelim.

Thread safe olmayan kod örneği

Bu kod parçacığı Thread safe bir kod değildir.Eğer iki farklı thread yakın zaman dilimlerinde bu sınıftan Instance methodunu çağırırsa iki ayrı nesne oluşur.Singleton deseninin gereklilikleri sağlanmamış olur.Bu kod tamamiyle teorik olarak singleton desenini uygulamıştır.Kullanılması önerilmez.

Thread Safe kod örneği

Singleton deseninin bu versiyonu thread-safe versiyonudur. Thread paylaşılan bir nesne üzerinden (lock) kilitlenir ve nesne örneğinin yaratılıp yaratılmadığını kontrol eder.

Lazy initialization

Lazy initialization bir nesnenin yaratılmasını geçiktirmek için bir taktiktir.Nesne ihtiyaç duyulacağı son ana kadar nesne yaratılmaz.NET 4 ile Base Class Library içerisine dahil olan generic Lazy tipi ile Lazy Initialization  işlemini gerçekleştiriyor.Bu tipi kullanarak singleton desenini kolaylıkla uygulayabiliriz.

Generic Singleton

Singleton desenini uygulamak istediğimizde , varolan sınıfın düzenlememiz gerekir.Tipten bağımsız bir şeklide bir sınıf singleton yapmak istersek  generic singleton ile kolaylıkla bunu gerçekleştirebiliriz.

Gerçek hayat örneği

Uygulamalarımızda kullanıcının yaptığı işleme göre hata mesajı , uyarı mesajı veya işlemin sorunsuz tamamlandığına dair bir mesaj veririz.Verdiğimiz bu mesajları kodumuza doğrudan yazmak yerine bunun için bir key tanımlayıp bu key e karşılık gelen string ifadeyi uygulamanın veri kaynağında key-value şeklinde tutmak istediğimiz düşünün ve bunun için de gerekli olan StringManager sınıfını yazdık.Fonksiyonel olarak düşündüğünüzde StringManager sınıfına uygulamanın her yerinden (app , uı) istek de bulunulabilir ve istenildiği kadar nesne oluşturulabilir.Bu aşırı kaynak üretimine ve uygulama yavaşlamasına sebep olacaktır.Bu sınıfı aşağıdaki şekilde singleton olarak tasarladık.

İlişkili olduğu desenler

  • Abstract Factory daki gerekli görülen ConcreteProduct sınıfları singleton tasarlanır.
  • Factory Method
  • Builder 
  • Durum sınıfları sıklıkla singleton olarak tasarlanır.
  • Alt sistemleri oluşturan Facade tipleri singleton tasarlanır.

Sonlandırırken

Singleton deseni genel olarak test edilmesi zor olduğundan mümkün olduğunda kullanmamak gerekir ve bu sebeple çoğunlukla anti-pattern olarak algılanır.Bunun yerine IoC Container gibi yapıları kullanmak daha efektif bir çözüm olarak görülür.

Peki neden bir sınıfı static yerine singleton tasarlamalıyım ?

Genel olarak bakıldığında singleton deseninin sağladığı fonksiyonelliği static sınıfların da karşıladığını düşünebiliriz.Fakat static sınıflar singleton yapılardan tamamiyle farklıdır.Şimdi bu yapılar hakkındaki bir kaç önemli noktaya bakalım.

  • Static sınıflar sistemdeki global verilere erişim için kullanılabilir. hızlı erişim ve performans sağlar
  • Singleton sınıflar geleneksel sınıf yapısını korur , static sınıfların kullanılması nesne yönelimli prensiplerden uzaklaştırır.
  • Singleton tasarladığımız sınıflar üzerinden kalıtım yapabiliriz diğer taraftan static sınıflarda kalıtım yapılamaz
  • Singleton ihtiyaç olduğu durumlarda lazy olarak tasarlanabilir.

Şimdilik bu kadar. Aşağıda kaynak kodları ve dökümanları bulabilirsiniz.Yazıda yanlış olarak ifade edilen noktaları görürseniz lütfen yorumlayın 😀

Leave a Reply

Your email address will not be published. Required fields are marked *