الگوی طراحی Singleton در سی شارپ

شنبه 17 تیر 1396

در این مقاله قصد داریم با الگوی طراحی Singleton یا همان Singleton Design Pattern آشنا شویم و انواع پیاده سازی آن را مورد بررسی قرار دهیم تا بدانیم چه زمانی باید از این الگوی طراحی استفاده کنیم.

الگوی طراحی Singleton در سی شارپ

در این مقاله می خواهیم موارد زیر را مورد بررسی قرار بدهیم :

1) الگوری طراحی Singleton چیست ؟

2) چگونه الگوی طراحی singleton را در c#   پیاده سازی کنیم؟

3) اثرات multithreading  بر روی الگوی Singleton 

4) پیاده سازی Double checked locking برای Singleton

5) نمونه سازی singleton به روش قدیمی

6) نمونه سازی singleton به صورت Lazy 

7) پیاده سازی Singleton با استفاده از Generic ها

8) نمونه سازی Lazy<T> در Singleton 

9) راه های شکستن Singleton

10) استفاده از الگوی Singleton 

11) Singleton به عنوان یک Anti-Pattern   

12) فرق بین کلاس های استاتیک و الگوری Singleton 

الگوری طراحی Singleton یا Singleton Design Pattern   

Singleton تضمین می کند که تنها یک نمونه از یک کلاس ساخته شود و در سراسر برنامه قابل دسترس باشد.

پیاده سازی استاندارد Singleton 

برای تضمین این که تنها یک نمونه از کلاس ساخته شده است ابتدا دسترسی سازنده کلاس را private قرار می دهیم، بنابراین شما میتوان تنها در داخل کلاس یک نمونه از خودش ایجاد کنید.
حال یک متغیر استاتیک ایجاد می کنیم که یک نمونه از کلاس را درون خود نگه دارد.
سپس یک متد استاتیک ایجاد می کنیم که یک نمونه از کلاس را برای ما فراهم می کند.این متد ابتدا بررسی می کند که نمونه ای از کلاس در دسترس است و اگر نمونه آن در دسترس نبود یک نمونه از آن را می سازد و در اخر نمونه ای که در دسترس است را باز می گرداند.

public class Singleton {
 
 private static Singleton instance;
 
 private Singleton() {}
 
 public static Singleton GetInstance(){
 if(instance == null){
 instance = new Singleton();
 }
 return instance;
 }
}

مشکلات پیاده سازی استاندارد

در پیاده سازی استاندارد Singleton کد ها در محیط تک تردی (single thread) به خوبی کار می کنند اما در سیستم چند تردی یا multithreaded کدهای متد ()GetInstance  شکست می خوردند.

public static Singleton GetInstance(){
 if(instance == null){
 instance = new Singleton();
 }
 return instance;
 }

اگر دو ترد هم زمان وارد  if شوند، دو نمونه از کلاس Singleton   ساخته می شود.

مدیریت مسئله multithreading  با پیاده سازی استاندارد Singleton 

ما میتوانیم متد را هماهنگ کنیم به طوری که تنها یک ترد دریک زمان به آن دسترسی داشته باشد. در پیاده سازی زیر، ترد مانع به اشتراک گذاری شی می شود و قبل از این که بخواهد از کلاس نمونه ای بسازد بررسی می کند که نمونه دیگری در دسترس نباشد. 

 public sealed class Singleton
    {
        private static Singleton instance = null;
        private static readonly object Instancelock = new object();
 
        private Singleton() {}
 
        public static Singleton GetInstance
        {
            get
            {
                lock (Instancelock)
                {
                    if (instance == null)
                    {
                        instance = new Singleton();
                    }
                    return instance;
                }
            }
        }
}

Double Checked Locking

در این روش، ابتدا بررسی خواهیم کرد که آیا نمونه ای ساخته شده است؟ اگر نه آن را هماهنگ کنید.

public sealed class Singleton
    {
        private static Singleton instance = null;
        private static readonly object Instancelock = new object();
 
        private Singleton() {}
 
        public static Singleton GetInstance
        {
            get
            {
                if (instance == null)
                {
                    lock (Instancelock)
                    {
                        if (instance == null)
                        {
                            instance = new Singleton();
                        }
 
                    }
                }
                return instance;
            }
        }

نمونه سازی به روش قدیمی

زمانی که کلاس بارگذاری شد میتوانیم یک نمونه از کلاس Singleton بسازیم. به این روش thread-safe می گویند که بدون استفاده از locking انجام می گیرد.

public class EarlySingleton {
 //create instance eagerly
 private static EarlySingleton instance = new EarlySingleton();
 
 private EarlySingleton() {}
 
 public static EarlySingleton GetInstance(){
 return instance;//just return the instance
 }
}

نمونه سازی singleton به صورت Lazy 

در این روش، نمونه سازی توسط یکی از اجزای استاتیک کلاس GetInstance انجام می شود که تنها یک نمونه ایجاد می شود.

public sealed class Singleton
  {
    private Singleton()
    {
    }
 
    public static Singleton GetInstance { get { return GetInstance.instance; } }
        
    private class GetInstance
    {
        // Explicit static constructor to tellcompiler
        // not to mark type as beforefieldinit
        static GetInstance()
        {
        }
 
        internal static readonly Singleton instance = new Singleton();
    }

پیاده سازی Singleton با استفاده از Generic ها

در این روش یک نمونه  بازگشت داده می شود و به طور موثر lazy می باشد چون سازنده استاتیک آن تا زمانی که Build()   فراخوانی نشود آن هم فراخوانی نمی شود.

public class GenericSingleton<T> where T : new()
    {
        private static T ms_StaticInstance = new T();
 
        public T GetInstance()
        {
            return ms_StaticInstance;
        }
    }
 
...
    GenericSingleton<SimpleType> instance = new GenericSingleton<SimpleType>();
    SimpleType simple = instance.GetInstance();

استفاده از نوع <Lazy<T 

شما نیاز برای ارسال delegate  به سازنده دارید که توسط سازنده Singleton فراخوانی می شود که این کار با استفاده از lambda expression به راحتی قابل انجام است.

public sealed class Singleton
{
    private static readonly Lazy<Singleton> lazy =
        new Lazy<Singleton>(() => new Singleton());
    
    public static Singleton GetInstance { get { return lazy.Value; } }
 
    private Singleton()
    {
    }
}

شکستن Singleton جز سوالاتی که هنگام مصاحبه از شما پرسیده می باشد.

حتی اگر ما با استفاده از Double checked locking یا نمونه سازی Eager  مانع از ساختن چند نمونه بشویم، توسط موارد زیر این امکان شدنی است :

-cloning 
-reflection 
-sub-classing singleton class

چگونه از cloning برای ساختن نمونه جلو گیری کنیم ؟

میتوانیم یک کپی با استفاده از متد Clone() ایجاد کنیم.

برای جلوگیری از ایجاد یک کپی از کلاس  Singleton میتوان موارد زیر را انجام دهید:
-پیاده سازی MehtodwiseClone() 
-Override کردن متد Clone() و throw کردن یک استثنا

protected object MemberwiseClone()
    {
        throw new Exception("Cloning a singleton object is not allowed");
    }

چگونه از ساخت نمونه توسط reflection جلو گیری کنیم؟

برای جلوگیری از ایجاد نمونه توسط reflection در سازنده کلاس بررسی کنید که اگر نمونه ای ساخته شده است، یک استثنا throw کنید.

نحوه جلوگیری از ایجاد نمونه توسط subclass ها

زمانی که سازنده کلاس شما private باشد کلاس های دیگر نمی توانند ازآن برای ایجاد نمونه ارث بری کنند.

استفاده از الگوری طراحی Singleton

لاگ زدن :

الگوی طراحی Singleton گزینه خوبی برای کلاس های logger است زمانی که ما میخواهیم یک فایل لاگ ایجاد کنیم و پیامی را درون آن قرار بدهیم این کار میتواند مفید باشد.اگر ما نمونه های بیشتری از logger در برنامه داشته باشیم، یک لاگ جدید توسط دیگر نمونه ها نیز ایجاد می شود.

کش :

ما میتوانیم با استفاده از این الگوی طراحی، کش هایی را پیدا سازی کنیم که تنها در کل سطح برنامه در دسترس است.

چرا الگوی طراحی Singleton یک Anti-Pattern در نظر گرفته می شود؟

1)الگوی Singleton را نمی شود به راحتی در Unit Testing مدیریت کرد.
2) حافظه ای که توسط Singleton اختصاص داده شده است نمی تواند آزاد شود.
3) در محیط MultiThreaded، ممکن است یک شی از Singleton حفظ شود.
4) به دلیل این که Singleton به کلاس ها وابسته است به سختی میتوان آن را تست کرد.

فرق بین الگوی Singleton و کلاس های استاتیک

1) درکلاس های استاتیک نمی توان اینترفیس پیاده سازی کرد. زمانی یک نمونه از کلاس نیاز به پیاده سازی اینترفیس دارد که اهدافی برای Ioc داشته باشد، شما می توان از Singleton بدون کلاس های استاتیک استفاده کنید.

2) شما میتوانید یک شی از Singleton را کپی کنید اما نمی توان یک شی از کلاس استاتیک را کپی کنید.

3) Singleton در Heap ذخیره می شود اما کلاس استاتیک در Stack ذخیره می شود

4) Singleton می تواند به صورت Layzily و asynchronously  با شد در حالی که کلاس های استاتیک برای اولین بار بارگذاری می شوند و به صورت عمومی مقداری دهی می شوند.

آموزش سی شارپ

برنامه نویسان

نویسنده 3355 مقاله در برنامه نویسان
  • C#.net
  • 8k بازدید
  • 8 تشکر

کاربرانی که از نویسنده این مقاله تشکر کرده اند

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید