توی این قسمت به معرفی یک الگوی ساده می پردازیم.الگوی Singleton از دسته الگوهای ایجادی.
خاطرم هست در مصاحبه استخدامی داتین از من راجع به دیزاین پترن ها و میزان آشناییم باهاشون، پرسیده شد و من خودم برای اینکه پیش دستی کنم و کار رو به جایی نرسونم که طرف مقابلم بخواد راجع به یک الگوی خاصی که توی ذهنشه ازم سوال کنه، بعد از یک مقدمه کلی از دیزاین پترن ها،بدون اینکه ازم خواسته بشه، شروع کردم به توضیح دادن Singleton که مثلا Singleton اِله و بِله.
مصاحبه به خیر و خوشی به پایان رسید و اون موقع فکر میکردم عجب!. اینقدر خوب گفتم که فهمید دیزاین پترن ها رو بلدم و دیگه خودش رفت سراغ سوال بعدی.
ولی الان که کمی عمیق تر شدم روی این مباحث، میفهمم اون وسط های "اِل و بِل" گفتن هام، یک عالمه "جیم بل" هم تحویلش دادم! و احتمالا صرف نظر کردن مصاحبه کننده از ادامه سوال درباره دیزاین پترن ها، ناشی از ریختن کُرک و پر اش بوده!
مخصوصا که بین Singleton و کلاس استاتیک پیوند ژنیتیکی پیدا کردم!
حالا به هر حال، اینو گفتم که بگم اصولا دیزاین پترن ها چیز حفظ شدنی نیست.درک کردنیه و سعی کنید درک کنیدش و محل استفاده اش رو پیدا کنید.
الگوی Singleton برای برآورده کردن این دو هدف معرفی شده که :
1- از یک کلاس تنها و تنها یک شی ایجاد شود.
2- امکان دسترسی سراسری در اپلیکیشن هم برای آن یک دونه شی وجود داشته باشد.
علارغم راه های مختلفی که برای پیاده سازی این الگو وجود داره، در حالت کلی کلاس هایی از الگوی Singleton دارن پیروی میکنند، دارای یک سری مشترکات اساسی هستند:
1- یک سازنده با سطح دسترسی Private که هدف اش محدودیت برای ایجاد بیشتر از یک instance یا شی از کلاس هست.
2- یک متغیر Static از جنس خود کلاس هستند که این متغیر در واقع همون یک دونه شی موجود ما هست.
3- یک متد Static با سطح Public که کارش برگردوندن همون متغیر بند دوم هست. این متد Static همون سطح دسترسی سراسری رو ب--رای instance ایجاد شده به وجود میاره.
همانطور که در بخش قبل اشاره شد گفتیم که راه های مختلفی برای پیاده سازی Singleton وجود داره. این تفاوت ها عمدتا در زمان ایجاد شدن شی است. یعنی :
1- شی در زمان تعریف خود متغیر Static ایجاد بشه. (Eager initialization)
2- شی داخل متد Static ایجاد بشه. (Lazy initialization)
3- شی درون داخل سازنده Private ایجاد بشه.
و تفاوت های بعدی این پیاده سازی ها هم در :
1- نحوه چک کردن عدم وجود Instance از کلاس
2- دفعات چک کردن وجود نداشتن شی است.
که این بحث چک کردن، برای سیستم هایی مهم می شود که یا دارای Multi threading هستند. یا از طریق reflection قراره کلاس ازش استفاده بشه و ... .
یعنی دغدغه سیستم های خاصی است که safe بودن از بابت اینکه مطمئن باشیم فقط و فقط یک شی در کل برنامه ایجاد میشه، اهمیت پیدا میکنه.
من یک نمونه ساده و استاندارد این موضوع از Singleton رو مثال میارم :
public sealed class SingletonSample { private static SingletonSmaple _instance; private SingletonSmaple() {} public static SingletonSmaple GetInstance { get { if (_Instance == null) _instance = new SingletonSample(); return _instance; } }
سوال : Singleton کجاها به درد ما میخوره؟
عموما Singleton در سناریو هایی مطرح میشه که ما در مورد دسترسی به منابع مشترک در سامانه دغدغه داریم. مثلا دسترسی به دیتابیس. اگر کانکشنی به دیتابیس هست و ایجاد شده، ما مجدد یک کانکشن جدید نسازیم که اون قبلیه به حال خودش رها بشه. هر وقت نیاز به ارتباط با بانک در جایی از بیزنس برنامه بود همون شی موجود رو ازش استفاده کنیم.
سوال : اگر قراره که فقط یک شی باشه با سطح دسترسی عمومی، خب چرا از کلاس Static استفاده نکنیم؟ چرا الگوی Singleton؟
کلاس Static درسته که ازش فقط و فقط میشه یک instance یا شی تولید کرد و این شی رو به صورت عمومی ازش استفاده کرد. با اما یک سری تفاوت های عمده با کلاسی که از الگوی Singleton پیروی میکنه داره :
اولا یک کلاس Static نمیتونه یک اینترفیس رو پیاده سازی کنه.
دوما از یک کلاس Static نمیشه ارث برد.
سوما خود کلاس Static هم نمیتونه child یک کلاس دیگه باشه. حتی اگر کلاس پدرش هم static باشه.
چهارما یک کلاس static رو نمیشه Dispose کرد.
پنجما که یک کلاس با الگوی Singleton از Lazy loading یا به عبارتی Lazy Initializing میتونه استفاده کنه ولی کلاسی که Static هست حتما در زمان Load اپلیکیشن ازش شی ساخته میشه.
ششما کلاس با الگوی Singleton میتونه سازنده داشته باشه. درصورتیکه کلاس Static بابت سازنده محدودیت داره.
هفتما متد های یک کلاس Static قابلیت Override شدن رو ندارن.
سوال : اون Sealed اون بالا توی کدی که نوشتی واسه چیه؟
کلمه کلیدی Sealed باعث میشه که من مانع از این بشوم که از کلاس ایجاد شده طبق الگوی Singleton خودم کسی ارث ببره.