مقدمه:
الگوی سینگلتون (Singleton - ترجمه این کلمه را در زبان فارسی می توان، به عبارتی "تک قلو" عنوان کرد. لیکن در ادامه این مطلب، جهت جلوگیری از پیچیده شدن جملات، از همان عبارت سینگلتون استفاده میکنیم) یکی از پر استفاده ترین، محبوبترین و شناخته شده ترین الگو های طراحی (Design Patterns) در مباحث برنامه نویسی می باشد (جهت آشنائی بیشتر با الگوهای طراحی، به این لینک مراجعه فرمائید). ایده اصلی این الگو آن است که یک کلاس تنها می تواند یک شیء داشته باشد، بدین ترتیب تنها یک نقطه دسترسی عمومی از نمونه شیء ایجاد می گردد. این مسئله به مقدار زیادی برای اشیاء state و global resource مفید واقع خواهد شد. در ادامه به روش های مختلف ساختن سینگلتون ها و در خلال آن نحوه ساختن یک کلاس سینگلتون عمومی (جنریک - generic singleton class) خواهم پرداخت...
سایت های متعدد و متفاوت زیادی در دنیای اینترنت وجود دارد که شما میتوانید اطلاعات مُکفی در باب سینگلتون ها و نحوه ساختن آنها برای هر نوع زبان برنامه نویسی ِ مورد نظر خود پیدا کنید. یکی از بهترین منابع موجود کتاب --- "الگوهای طراحی: المان های نرم افزارهای شیء گرا، با قابلیت استفاده مجدد" (Design Patterns: Elements of Reusable Object-Oriented Software) از همان GoF معروف (Gang of Four) می باشد.
یکی از نگرانی های اصلی موجود در هنگام استفاده و یا حتی ایجاد سینگلتون ها، اپلیکیشن ها یا محیط های چند رشته ای (Multi-Thread) می باشد. این مشکل هنگامی روی خواهد داد، که دو thread تصمیم میگیرند از یک سینگلتون هرگز ساخته نشده نمونه ای دریافت کنند، بنابراین آنها این شانس را خواهند داشت که دو نمونه شیء مجزا ایجاد نمایند. تمام روش هایی که در این مقاله آورده شده است، از این بابت ایمن بوده و به عبارتی می توان گفت thread safe هستند.
روش اول:
اولین روش ارائه شده، کلاس سینگلتونی میباشد که میتوان دیگر کلاس ها را بر طبق آن قالب و پیکر بندی کرد. در این روش نمونه شیء سینگلتون در همان ابتدای آغاز به کار اپلیکیشن ایجاد میشود. که همین امر باعث عدم ایجاد اخلالی از نوع گفته شده در پاراگراف قبل خواهد شد. یعنی سیستم به نوعی thread safe خواهد بود. همانطور که در قطعه کد زیر مشاهده خواهید کرد، این کلاس یک سازنده کلاس (constructor) از نوع private دارد که همین امر باعث میشود که کلاس نتواند در خارج ایجاد شود. یک متغیر از نوع static private جهت نگهداری تنها نمونه شیء کلاس ما در داخل متد تعریف میشود که نهایتا شیء را بر میگرداند.
public class Singleton1 { private static Singleton1 instance = new Singleton1();
private Singleton1(){}
public static Singleton1 GetInstance() { return instance; } }
|
نهایتا تمام کاری که باید جهت ایجاد یک نمونه شیء از این کلاس انجام شود، همین یک خط کد خواهد بود:
Singleton1 single = Singleton1.getInstance();
|
روش دوم:
روش دوم از نمونه سازی تنبل (lazy instantiation) استفاده می کند. به این صورت که تا زمانی که به کلاس نیازی نیست، ایجاد نخواهد شد. این روش زمانی مفید است که کلاس شما تنها در موارد نادر استفاده می شود. در قطعه کد زیر مشاهده خواهید کرد که ما مجددا متغییری از نوع static private داریم، با این تفاوت که بر عکس روش اول، در اینجا متغییر ما به هیچ نمونه شیء جدیدی ست نشده است. مجددا یک constructor خالی و متد ()GetInstance را در این روش خواهیم داشت؛ با این تفاوت که اینبار مقدار کد بیشتری در مِتُدمان مشاهده خواهد شد.
اولین چیزی که در این متد خواهید دید، فرمان lock با پارامتر (typeof(Singleton2 می باشد. این فرمان قطعه کد موجود در این بلاک را برای هر thread در یک زمان مشخص قفل کرده، منحصر به آن thread نگاه خواهد داشت. آیتم بعدی یک دستور شرط (if statement) جهت چک کردن این است که آیا نمونه شیء ما هنوز ساخته شده است یا خیر. که بر طبق شرط، اگر نمونه null بود، ما یک نمونه ایجاد خواهیم کرد. و در نهایت، ما فقط نمونه شیء را بر می گردانیم. و جهت نمونه گیری از کلاس مانند همان قطعه کدِ تک خطی در روش اول، عمل خواهیم کرد:
public class Singleton2 { private static Singleton2 instance;
private Singleton2() { }
public static Singleton2 GetInstance() { lock (typeof(Singleton2)) { if (instance == null) { instance = new Singleton2(); } return instance; } } }
|
روش سوم:
آخرین روش پیاده سازی یک کلاس سینگلتون که در اینجا به آن اشاره خواهم کرد، یک سینگلتون عمومی است. این روش دقیقا مانند روش دوم است، با این تفاوت که در اینجا از یک syntax عمومی جهت ایجاد سینگلتون از هر کلاسی استفاده می شود. با این حال، این کار از ساخته شدن کلاس T (در قطعه کد زیر) توسط خودش، جلوگیری نخواهد کرد. بنابراین استفاده از این نوع پیاده سازی بر اساس ماهیتی که دارد، بسیار مفید خواهد بود. لیکن، ضعفی که این روش دارد این است که قادر به اعمال کنترل مطلق بر روی سینگلتون نمی باشد.
class GenericSingleton<T> where T : class, new() { private static T instance;
public static T GetInstance() { lock (typeof(T)) { if (instance == null) { instance = new T(); } return instance; } } }
|
جهت استفاده از این کلاس در بدنه برنامه، از قطعه کدِ تک خطی زیر استفاده می کنیم. اینطور فرض میکنیم که کلاسی با نام AutoFactory تعریف کرده ایم. سازنده (constructor) کلاس AutoFactory، نیز در این نوع پیاده سازی قادر به گرفتن پارامتر نخواهد بود.
AutoFactory autoF = GenericSingleton<AutoFactory>.GetInstance(); |
امیدوارم این مطلب در نوع خود، باعث آن شده باشد که شما عزیزان، درک صحیح و مناسبی از الگوی طراحی سینگلتون و نحوه پیاده سازی آن با زبان #C بدست آورده باشید. در صورت وجود هرگونه پیشنهاد، احیانا انتقاد از نوع ارائه مطلب و نحوه نگارش آن و یا هر نوع مطلب دیگری که مد نظر خودتان می باشد، از باکس زیر استفاده بفرمائید.
موفق باشید و موید.