رفع ابهاماتی درباره‌ی دیراین پترن Singleton در سی شارپ

زمان تقریبی موردنیاز برای مطالعه: ۳ دقیقهدیزاین پترن Singleton یکی از مواردی است که ابهامات زیادی را به وجود می آورد. چرا که تعریف سنتی آن یعنی «الگویی که در آن می توان از یک کلاس تنها یک شی ساخت» چندان صحیح نیست و باید گفت اصولاً در دیزاین پترن سینگلتون هدف این هست که نتوان از روی آن کلاس بیشتر از یک شی ساخت اما این عمل با استفاده از ایجاد یک «اشاره گر» static از نوع کلاس موردنظر حاصل می شود. نه این که امکان نمونه سازی از کل کلاس از بین برود.

 
کلاس زیر که ساده ترین پیاده سازی سینگلتون (lazy instantiation) است را در نظر بگیرید:

برای مقدار دهی به شی instance در هر کجای کد می توان به صورت زیر عمل کرد:

اگر مبحث برنامه نویسی چندنخی را در نظر نگیریم (چرا که lazy instantiation ساده است و thread-safe نیست)، فیلد instance تنها یک بار نمونه سازی می شود و در دفعات بعد صرفا به آن اشاره می شود. هر چند اشاره گر static است اما شی ای که به آن اشاره می کند dynamic است و static بودن اشاره گر آن دلیلی بر تفاوت شی اشاره شده با یک شی معمولی ندارد.
پس: تک بودن شی به دلیل این است که تنها یک بار ساخته می شود و static بودن محدود به اشاره گر شی می شود. چون اساساً شی static معنی ندارد!

شاید بیشتر این ابهام به دلیل این باشد که برای جلوگیری از اسپاگتی شدن کد این تعریف در بدنه ی خود کلاس صورت می پذیرد. اگر فیلدهای سینگلتون در کلاسی جداگانه تعریف شوند شاید فهم آن ساده تر شود:

بعد از این توضیح باید به چهار سوال پاسخ داد:
۱- پس امکان نمونه سازی از کلاسی که در آن دیزاین پترن سینگلتون پیاده سازی شده وجود دارد؟
بله، قطعا هیچ دلیلی برای کار نکردن قطعه کد

وجود ندارد و فیلدهای secondSingleton می توانند فارغ از نمونه ی Instance مقداردهی شوند و خوشبختانه دسترسی به فیلد Instance از طریق شی secondSingleton وجود ندارد. چرا که آن فیلد static است و امکان دسترسی به آن از طریق شی (در سی شارپ) وجود ندارد. هر چند که با اعمالی نظیر تعریف یک متد constructor با سطح دسترسی private می توان از امکان نمونه سازی کلاس جلوگیری کرد اما ربط چندانی به دیزاین پترن سینگلتون ندارد.
۲- امکان تعریف متد در این کلاس وجود دارد و اگر این امکان هست مقادیری که متد از فیلدها باز می گرداند مربوط به نمونه ی instance هستند و یا نمونه ای که به صورت دستی ساخته شده؟
بله. امکان تعریف متد در کلاس وجود دارد. متد فرضی زیر را در نظر بگیرید:

اگر متد از طریق Instance صدا زده شود یعنی:

فیلد Instance و اگر از طریق شی دست ساز صدا شود یعنی:

فیلد شی را بر می گرداند.
۳- گفته شد که در سی شارپ امکان دسترسی به فیلد استاتیک توسط شی وجود ندارد. اما این امکان در زبان جاوا وجود دارد. آیا این باعث ایجاد خطا نمی شود؟
این یک تناقض در زبان جاوا با مفاهیم شی گرایی است. اما آن گونه که به نظر می رسد نیست و در جاوا هم این امکان وجود ندارد (چرا که اگر وجود داشت موجب ایجاد بی نهایت Instance و کرش کردن می شد). هر چند جاوا این امکان را می دهد که از طریق شی بتوان به فیلد static دسترسی داشت اما javac (کامپایلر جاوا) آن شی را کلاسی از نوع شی در نظر می گیرد. البته بهتر است که در جاوا هم این کار را انجام ندهیم.
۴- آیا نمی شد همین کارها را با استفاده از یک کلاس static انجام می دادیم؟
در اینجا تفاوتهایی از این دو را بررسی می کنیم:
  • شی سینگلتون در حافظه ی heap ذخیره می شود (چرا که شی است)، اما کلاس static در حافظه ی stack که می تواند باعث تاثیر در پرفورمنس شود.
  • امکان ساخت شی از روی کلاس سینگلتون وجود دارد (که در سوال اول بررسی کردیم)، اما امکان ساخت شی از روی کلاس static وجود ندارد.
  • و اما مهم ترین تفاوت: کلاس سینگلتون می تواند اینترفیس implement کند و یا در ارث بری شرکت کند. اما این امکان برای کلاس های static وجود ندارد.