Singleton Design Pattern Nedir?

Creational Patterns (Oluşturulma Tasarım Kalıpları) ailesinden Singleton Pattern (Tek Nesne Kalıbı)‘e göz alalım.

Kullanım Amacı
Singleton pattern’in kullanım amacı ortak kullanılan nesneleri bir kere oluşturarak maliyeti azaltmak ve performansı arttırmaktır. Singleton nesneler static olmaktadır. Uygulama ayağa kalktığında gelen ilk istekle Ram’de oluşur ve daha sonraki istekler ise ilk oluşturulan nesne üzerinden işlem yapmaya devam eder. Kullanım alanı olarak ilk başta da belirttiğimiz gibi projede ortak olarak kullanılan alarda kullanılır. Örneğin veri tabanı bağlantılarında, dosya işlemlerinde,port bağlantılarında, loglama işlemlerinde, bildirimlerde, iş katmanı servislerimizde ve buna benzer birçok yerde kullanılmaktadır.
Tek bir nesneye ihtiyaç duyduğumuz zamanlarda kullanırız.

Bir nesne örneğinden sadece bir kere üretilip ve bu nesne örneğinin her zaman kullanılmasını ön gören bir patterndir. Örneğin bir web sitesinde online ziyaretçi sayısını göstermek istiyoruz burada herkese aynı değeri göstermemiz gerekmektedir. En büyük hedeflerden biride nesnenin state nin kontrol edilmesidir.

Örnek Kullanım:
İlk olarak singleton olmadan örneğimizi aşağıdaki gibi yazıp çalıştırıyoruz.



    class Program
    {
        static void Main(string[] args)
        {
            for (var i = 0; i < 20; i++)
                Console.WriteLine(Singleton.CurrentGuid);

            Console.ReadLine();
        }
    }

    class Singleton
    {
        private static string _currentGuid;

        public static string CurrentGuid
        {
            get
            {
                _currentGuid = Guid.NewGuid().ToString();
                return _currentGuid;
            }

        }
    }


Çıktıda gördüğünüz gibi guid için farklı farklı değerler oluşturuyor. Eğer Guid değerinin her seferinde aynı değeri dönmesini istersek Guid değeri bir kere set edilecek ve diğer gelen isteklerde bu değer döndürülecek.
Singleten sınıfını aşağıdaki gibi düzenleyip tekrar sonucu inceleyelim.


  class Program
    {
        static void Main(string[] args)
        {
            for (var i = 0; i < 20; i++)
                Console.WriteLine(Singleton.CurrentGuid);

            Console.ReadLine();
        }
    }

    class Singleton
    {
        private static string _currentGuid;
        static object _lock = new object();
        public static string CurrentGuid
        {
            get
            {
                lock (_lock)
                {
                    if (_currentGuid != null)
                        return _currentGuid;
                    lock (_lock)
                    {
                        _currentGuid = Guid.NewGuid().ToString();
                    }
                    return _currentGuid;
                }
            }

        }
    }


Çıktıda listelenen değerlerin hepsinin aynı olduğunu görüyoruz. CurrentGuid property her çağrıldığında _currentGuid değerinin null olup olmadığı kontrol ediliyor. Eğer null değilse direkt ramde tutulan değer geri gönderiliyor. Eğer null ise değerimizi oluşturup geri gönderiyor ve bu istekten sonra ki tüm isteklerde bu değer gönderilecek şekilde ram de tutuluyor.
Singleton tasarım kalıbının amacı nesnenin bir kere oluşturulması olduğundan lock ile şöyle bir durumun önüne geçmeye çalıştık.
Bu nesne birden çok yerden aynı anda çağırılmış olabilir. null kontrolünü aynı anda geçen birden fazla istek olabilir ve her gelen istek null kontrolünü geçtiği için birden fazla nesne oluşturmuş olur. Bu da singleton amacına uymayacağı için gelen istekleri sıraya koyup bekletiyoruz. Aynı anda birden fazla istek geldiğinde ilk gelen istek lock bloğuna girecek ve diğer istekler sırasını bekleyecektir. İlk istek işlemi yaptıktan sonra nesnemiz null olmayacağı için sonraki istekler singleton amacına uygun olarak ilk oluşturulan değeri alıp işlemine devam edecektir.
Bu durum genelde değeri set ederken uzun süren işlemlerde meydana gelebiliyor. Örneğin çalıştığım bir e-ticaret sitesinde ürün fiyatllarını ram’de tutuyorduk. Fiyat tablosunda farklı müşteri segmentleri ve indirimleri olduğu içinyaklaşık 6 milyon kayıt bulunuyor. Bu kayıtlarla ilgili hesaplamaları yapıp Ram de tutuyorduk. Bu işlem yaklaşık 2 – 3 dk sürüyordu. Eğer burada singleton kullanmasaydık her ürün fiyatıyla ilgili işlemde 2 – 3 dakikalık bir bekleme olacaktı ki bu beklenilmesi kabul edilecek bir süre değil. Aynı şekilde lock deyimini kullanmasaydık ilk gelen istek henüz bitmeden başka bir istek geldiğinde tekrar fiyatları çekmeye çalışacaktı ve bu da can yakacak bir durum haline gelecekti.
Bir örnek daha verelim...


 class Program
    {
        static void Main(string[] args)
        {
            var customerManager = CustomerManager.CreateAsSingleton();
            customerManager.Save();

            Console.ReadLine();
        }
    }

    class CustomerManager
    {
        // bir nesneyi aynı anda iki kullanıcı isterse ve bu nesne daha önce hiç oluşmadıysa
        // aynı nesneden iki tane oluşturulabilir bu olay singleton desing pattern e ayrıkı bir işlemdir
        // bu yüzden lock kullanımı ile bu olayın önüne geçeriz. 
        private static CustomerManager _customerManager;
        static object _lockObject = new object();

        public CustomerManager()
        {

        }

        public static CustomerManager CreateAsSingleton()
        {
            lock (_lockObject)
            {
                if (_customerManager == null)
                {                   
                        _customerManager = new CustomerManager();
                }
            }
            return _customerManager;

        }

        public void Save()
        {
            Console.WriteLine("Save !! ");
        } 
    
    }

Umarım yazı sizin için faydalı olmuştur.

Hiç yorum yok:

Yorum Gönder