Singleton Design Pattern

Singleton’ı şu iki maddede özetleyebiliriz.

  1. Bir sınıfın bir örneği olacağını garanti eder.
  2. Bu sınıfı global bir erişim noktası yapar.

Bir design pattern’ı kullanmadan önce onun zayıf yanlarını iyi bilmek gerek. Profesyonel hayatta, her yanından Singleton fışkıran kodlara gülündüğüne bir çok kez şahit oldum. Bu kahkahaların sebebini araştırırken okuduğum ve içlerinden notlar aldığım bir kaç yazı var. Singleton’ı uygulamadan önce bu notları sizlerle paylaşmak isterim.

Use Your Singletons Wisely (Singletonlarınızı Bilgece Kullanın)

Yazı, neyin gerçekten Singleton olması gerektiğini sorguluyor. Bu sorgulamayı yapmadan önce, Singleton’ın sınıflar arası bağımlığı artırdığını belirtiyor. Yani Singleton’ın göründüğü kadar masum olmadığı konusunda yazar ile aynı fikirdeyiz.

Yazının can alıcı yerine geldiğimizde Singleton uygulanmadan sorulması gereken 3 soru ile karşılaşıyoruz. Bana sorarsanız, bunlar bir hayli haklı sorular.

  1. Bu sınıfı bütün uygulamalar aynı şekilde mi kullanacak? “Aynı şekilde” kalıbı burada belirleyici unsur. – (Yanıt: Bu sınıf eğer bir Singleton’sa bütün uygulamalar tarafından aynı şekilde kullanılmalılar. )
  2. Bu sınıfı kullanacak her uygulamanın bu sınıfın her zaman ve sadece bir örneğine mi ihtiyacı olacak? (Yanıt: Bu sınıf eğer bir Singleton’sa bu sınıftan sadece bir adet instance yani örnek olmalı. Bu sınıfı kullanacak her uygulama, her zaman bu sınıfın sadece bir örneğine ihtiyaç duyacak.)
  3. Bu sınıfın istemcileri bir parçası oldukları uygulamadan bihaber olmalılar mı? (Yanıt : Evet, bu sınıfın istemcileri parçası oldukları uygulamadan bihaber olmalılar.)

Bu üç soruya yanıtımız evetse, bu sınıfı Singleton yapmakla doğru yapıyoruz.

IBM’in bu yazısında örnek olarak bir Logger verilmiş. Logger belki de verilebilecek en güzel örnek. Bütün uygulamalarda, uygulamanın bütün parçalarında log bastırılmak istenebilir. Logger nesnesinin Singleton olmasında kesinlikle bir engel yok.

Why Singletons Are Evil

Burada da Singleton’ların şeytan olduklarına dair 4 kanıt sunulmuş. Aldığım notları sizlerle paylaşıyorum.

  1. Singleton’lar kodun her yerinden erişilebilirlerse, bu onları global değişkenlerin içinde bulunduğu duruma sokar. Global değişkenler kötüdür değil mi?
  2. Singleton’lar nesne yaratmayı sınırlandırmanıza izin verir. Bu da SOLID’in ilk kuralı Single Responsibility’e ters düşer. Oysa ki, Singleton bir sınıf, Singleton olup olmadığı ile ilgilenmemelidir. (Single Responsibility der ki, bir sınıfın yalnızca bir görevi olmalıdır. Yani Logger’ınız sadece log basmalıdır, gidip belleğe veri yazmamalıdır. )
  3. Singleton’lar sınıflar arasındaki bağımlılığı artırır. Bu da kodun test edilebilirliğini azaltır. Bu durumdan kurtulmak için nesnelerin içine referans yollama yoluna başvurabilirsiniz.
  4. Singleton’lar program sonlanana kadar durumlarını korurlar. Singletonlar gibi durumunu koruyan kod parçaları Unit Test’in düşmanıdırlar.

Patterns I Hate #1 Singleton

Singleton’ın neden kullanılmaması gerektiğini anlatan elimdeki en iyi yazı bu. Bu yazıdan aldığım ilk notsa, her okuduğumda çarpıcı bir etki bırakıyor bende.

“Henüz yolun başındaki geliştiriciler, yardımcı sistemlerini bozarak onlara Singleton yoluyla ulaşırlar.”

Yazının başında bahsettiğim kahkahaların sebebini bu yazıda buluyorum. Singleton kullanarak bir sisteme ulaşmak çok kolay. Ana bir sistemin yardımcı sistemlerine doğru kod akışını tasarlayamadığımız anda aklımıza bir kaçış şekli olarak Singleton düşüyor. Ve dolayısıyla yardımcı sistemimizi bozuyor ve ona Singleton yardımıyla ulaşıyoruz.

“Kod bağımlılığını saklarlar”

Yazar bu sözü açıklarken Singleton’ı Kudzu’ya benzetiyor. Kudzu bir Japon sarmaşığı. Kendi tecrübelerimden yola çıkarak konuşacak olursam, bir hayli haklı. Başta masum ve şirin duran, fakat projenin sonlarına doğru kodumun her yanını sarmış Singleton’lar geliyor aklıma. Kod bağımlılığının Singleton tarafından böyle ustaca saklanması, bu sorunun gözümüzden kaçmasına sebep oluyor ve dolayısıyla çözüm aramıyoruz.

“Test etmeyi zorlaştırıyor”

Daha önceki notlarda da bu duruma parmak basılmıştı. Singleton kod içindeki bağımlılığı artırdığı için, test edilebilirliği düşürüyor.

“Alt sınıflamaya uygun değil”

Singleton bir sınıftan bir alt sınıf türetmek, Singleton’ın doğasına aykırı. Singleton’lar statiktir, yani onlardan sadece bir tane bulunur. Hatta bunu garanti etmek için, yazdığımız Singleton’lara private bir constructor yazarız. Ki onlardan herhangi bir sınıf miras alamasın.

Paylaşacağım notlar burada son buluyor. Singleton ile ilgili biraz daha okuma yapmak isterseniz, yazının sonunda kaynak önerilerinde bulunacağım.

Artık Örneğimize Başlayabiliriz

Kendi çalışmalarımda kullandığım bir Logger’ı sizlerle paylaşacağım. LB_Logger‘a github profilimden ulaşabilirsiniz.

Adım 1 – Logger’ımızın ana ve alt bileşenlerini tasarlayalım.

Logger’ımız üç bileşenden oluşacak.

  • LB_Logger: Singleton olan nesnemiz bu. Bu nesne, Logger’ımızın ana bileşeni. Projenin herhangi bir yerinden kolayca ulaşılabilir.
  • LB_LoggerMonitor: Mobil cihazda uygulamamızdaki logları görmek istersek, monitor’ümüzü uygulamamızın ilk sahnesine eklememiz yeterli. Bu nesnenin görevi LB_Logger’a kendini kaydetmek ve basılan logları ekranda göstermek.
  • LB_LogSaver: Loglarımızı bir dosyaya kaydetmek istersek, sahnemize LogSaver nesnemizi ekliyoruz. LogSaver da logları bir text dosyasına kaydederek, daha detaylı bir döküman elde etmemizi sağlıyor.

Adım 2 – LB_Logger

Kodun akıllarda soru işareti oluşturabilecek yanlarını açıklayalım.

LB_Logger’ın bir örneğini tutuyoruz ve bu örnekten sadece bir tane olacağını garanti ediyoruz.

C# compiler’a beforefieldinit olarak işaretleme yapmamasını söylüyoruz. Böylece Singleton örneğimiz ilk ihtiyaç duyulduğu zaman yaratılıyor. Daha detaylı bilgi için bu yazıyı okuyunuz.

Singleton nesnemizin yaratılan örneğine ulaşımı da bu Instance ile sağladık.

Kodun herhangi bir noktasından, LB_Logger.Instance.PrintLog(“Test”) örneğindeki gibi bir log yollayabiliriz. LB_Logger bu logu alır ve basılan diğer loglara ekler. Ve ardından PrintLogEvent eventini dinleyen bütün dinleyicilere bu log stringini dağıtır.

Adım 3 – LB_Monitor

Log Monitörümüz, LB_Logger’a yollanan logları ekranda göstermeye yarıyor. Ne zaman aktif olsa LB_Logger’ın PrintLogEvent olayını dinliyor. Ne zaman deaktif olursa da dinlediği bu olaydan kendini siliyor.

Adım 4 – Logları kaydetmek ya da kaybetmek, işte bütün mesele bu.

Logları kaydetmemiz sebebini bir türlü bulamadığımız hatalara karşı bizi koruyabilir. Kaydedilen loglar çok daha rahat izlenirler ve dolayısıyla hataların bulunmasını kolaylaştırırlar. LB_LogSaver’ımız da bizim için logları kaydedecek.

Singleton hakkında okumaya devam etmek isteyenler için önerilerim.

Şurası da şöyle olsa daha iyi olurdu demekten, bunu yorum olarak belirtmekten lütfen çekinmeyin. Bana cosgun.halil@gmail.com adresinden ulaşmak konusunda da rahat hissetmenizi rica ediyorum.

Eğer bu çalışmam işinize yaradıysa ve daha fazla çalışma yapabilmem için bana destek olmak isterseniz, bir kahvenizi içerim. 🙂

Saygılar.