Singleton Pattern Kullanimi .NET Design Pattern



Design Patterns(Tasarım Desenleri) arasında Factory Pattern'dan sonra en yaygın kullanılan desenlerden bir taneside "Singleton Pattern" diyebiliriz. Peki, Singleton Pattern ne zaman tercih etmeliyiz ?
Bir nesnesinin projemiz ayağa kalktığında(runtime) tek bir örneğinin olmasını, yani tek bir instance olmasını istiyorsak eğer burda Singleton devreye giriyor. Bir nesneye single özelliğini bir kaç şekilde verebiliriz, fakat bir nesneye single özelliğini başka bir nesne veremez.
C#'da kullandığımız "HttpContext" signleton nesneye en güzel örneklerden bir tanesidir. Singleton özelliği almış olan nesnesinin yeni bir instance alınamaz. Peki, nesnesimize signleton özelliğini nasıl katabiliriz ? Bir önceki Factory Pattern Reflection Kullanımı yazımızda örneğini yapmış olduğumuz "LogManager" nesnesine Singleton özelliği katalım.

 public class LogManager
    {
        public static void Log(string value)
        {
            ILogger logger = LogFactory.GetLogger();
            logger.Log(value);
        }
    }

Bu görünümde "LogManager" nesnesime instance alabiliyoruz. Ama methodumuz static, burda bir mantık hatası var. Zaten bir loglama işlemi yapılacaksa eğer developer neden yeni bir nesne oluşturma gereği duysun öyle değil mi ? Var olan bir nesne üzerinden işlemini yapmaya devam etsin. Instance almak ona yeni bir özellik, yetenek, veri katmayacak sonuçta. Öncelikle nesnenin kendisinin "new LogManager()" diye yeni bir örneğinin alınmasını engellemek için constructor methodunu private olarak düzenliyoruz.

 private LogManager()
        {

        }

Nesnemizden yeni bir instance alınmasını engelledik. Şimdi bize tek bir instance vermesine ihtiyacımız var. Bunun için "LogManager" classının içinde yine kendi tipinden static bir property ihtiyacımız var. Biz sadece bu nesne üzerinden işlem yapmak istiyoruz.
C#'da HttpContext.Current aklınıza gelebilir. HttpContext.Current'da aslında bir "HttpContext" nesnesini temsil ediyor bildiğiniz gibi. Ben bunun iki farklı şekilde yapılabilmesini göstericem. Birincisi ilk akla gelen private static bir değişken tanımlayıp onun üzerinden hareket etmek.

        static LogManager _current;
        static object _locker = new object();
        public static LogManager Current
        {
            get
            {
                if (_current == null)
                {
                    lock (_locker)
                    {

                        if (_current == null)
                            _current = new LogManager();
                    }
                }
                return _current;

            }
        }

Bu yazdığımız düzen ile beraber artık LogManager instance alınamıyacak ve artık "LogManager.Current" olarak kullanılabilecek. LogManager instance alırken "_locker" kullandım. Bunu yapmazsak bize birden fazla thread'in çalıştığı ortamlarda sıkıntı yaratıcaktır.
Bu yüzden lock kullanmak zorundayız. Bunun beraber static "Log" methodumuzu instance alınacak(Singleton Instance) nesne üzerinden kullanılması için static özelliğini alıyoruz.

  public void Log(string value)
        {
            ILogger logger = LogFactory.GetLogger();
            logger.Log(value);
        }

Bu özelliğini almazsak eğer "LogManager.Log("S.Ç.")" olarak kullabilir. Bu bizim istemediğimiz bir durum, methodları sadece singleton nesne üzerinden kullanmalıyız. Yukarda yaptığımız nesneye signle özelliği katma yapısı genellikle kullanılan bir yapıdır. Ancak kullanımı daha kolay hale getiren C# üzerinde bir sınıf var.
Bu sınıfın adı "Lazy", Lazy sınıfı bizim için nesneye Signle özelliği katma konusunda oldukça faydalı bir sınıf. Kullanım olarak ilk yaptığımız örnekten çok daha sade. Kullanımı ise şu şekilde ;

        static Lazy _current = new Lazy(() => { return new LogManager(); }, true);
        public static LogManager Current
        {
            get
            {
                return _current.Value;
            }
        }

Lazy generic kullanımı ile verdiğimiz tipte bize single özelliği için uygun ortamı sağlıyor. Fark ettiyseniz eğer null kontrolü yapmadım ya da lock objesi kullanmadım.
Lazy nesnesi bunu bize sağlıyor, return new LogManager() dediğimiz bölümde "true" dediğimiz noktaya dikkat ederseniz eğer burda Lazy'nin "ThreadSafe" özelliği aktif etmiş oluyoruz. Bu bizim ilk yaptığımız kullanımdaki lock işlemine denk geliyor diyebiliriz. Son olarak artık developer şu şekilde kullanıcak ;

           try
            {

                throw new Exception("Exception patladı gitti");
            }
            catch (Exception ex)
            {
                string message = ex.ToString();
                LogManager.Current.Log(message);
            }

Böylece tek bir nesne üzerinden tüm loglama işlemleri yapılmış olucak. Bu bizim için memory performansını yükselticektir..

 

Yorum Yaz