با طعم کد!

سینگلتون دوست‌داشتنی! قسمت اول

چرا از سینگلتون استفاده کنیم، تا اینکه چگونه به بهترین شکل از آن استفاده کنیم!؟

الگوی سینگلتون(singleton) واقعا یکی از آسان‌ترین design patternهای ممکن است، اما باور کنید خیلی از ما (ما توسعه دهنده‌ها) به درستی این‌کار را انجام نمی‌دهیم! بگذارید کمی بیشتر توضیح بدهم:

همه ما می‌دانیم (یا حداقل من احتمال می‌دهم که همه ما بدانیم) که الگوی سینگلتون در چه مواردی و برای چه چیزی استفاده می‌شود، این الگو به ما اجازه می‌دهد تا فقط یه نمونه/نسخه (instance) از آبجکت یا کلاس مورد نظر خودمان در طول چرخه عمر برنامه داشته باشیم! شاید بپرسید که این موضوع اصلا چه اهمیتی دارد؟ اهمیتِ ساخت فقط یک instance را زمانی درک می‌کنیم که به این دو نکته دقت کنیم:

  1. ما نمیخواهیم که از یک آبجکت مصرف کننده منابع و resource، بیشتر از یک مورد بسازیم، در حالی که فقط به یک مورد از آن آبجکت احتیاج داریم.
  2. در بعضی مواقع علاوه بر اینکه ایجاد یک instance جدید، هزینه‌بر و مصرف‌کننده است، باعث وارد شدن به حالت ناپایدار و متناقضی می‌شود که مدیریت برنامه را سخت می‌کند. برای مثال شما نمی‌خواهید که چند instance  از آبجکت دیتابیس داشته باشید!! زیرا تغییر در یکی از آن‌ها می‌توانند دیگر instance ها را تغییر دهد!

اما ممکن است با خودتان فکر کنید حالا که فقط به یک نمونه ساخته شده از آبجکت مورد نظرمان احتیاج داریم و می‌خواهیم از همه قسمت‌های برنامه به آن دسترسی داشته باشیم، چرا یک متغیر سراسری یا global تعریف نمی‌کنیم؟ جواب این است که بله میتوانیم یک متغیر سراسری به این منظور تعریف کنیم! اما باید به این نکته هم دقت کنیم که علاوه‌بر این که تعریف متغیر عمومی و سراسری در ابتدای اجرای برنامه انجام می‌شود و در نتیجه اگر ایجاد instance از آبجکت مورد نظر شما نیازمند مصرف منابع زیادی باشد و یا اصطلاحا هزینه‌بر باشد، در شروع برنامه زمان زیادی برای مراحل اجرا گرفته خواهد شد(مخصوصا اگر توسعه‌دهنده موبایل هستید شاید بیشتر باید به مورد دقت کنید!)، باید به این مورد نیز توجه کنیم که حافظه اجرایی محدود است و نباید آن را در زمانی که به آن نیاز نداریم اشغال کنیم! پس بهتر است به جای این مورد سراغِ lazy-loading برویم، یعنی متغیرها را فقط و فقط زمانی تعریف کنیم که به آن نیاز داریم.

خب، حالا که همه با هم موافقیم که استفاده از singleton pattern خیلی از کارها را بهتر و دوست‌داشتنی‌تر خواهد کرد، بیایید ویژگی‌های یک singleton خوب را با هم مرور کنیم:

۱. یک کلاس سینگلتون خوب، نباید به دیگر کلاس‌ها اجازه ساخت یک instance را بدهد! به این معنی که هیچ کلاسی نباید قادر باشد خط کد جاوای new Singleton() و یا معادل آن در کاتلین که به صورت Singleton()  است را فراخوانی کند(در اینجا نام کلاس سینگلتون خودتان را Singleton در نظر بگیرید.). اما چطور باید مانع این اتفاق شد؟ خیلی ساده! 🙂 فقط کافیست که سازنده یا به اصطلاح constructor کلاس را به حالت دسترسی private تغییر دهید، که به این ترتیب کلاس سینگلتون ما از هیج کلاس خارجی دیگری قابل نمونه‌گیری نیست به جز خودش! مثال سازنده خصوصی را در زبان شیرین کاتلین میتوانید مشاهده کنید.

۲. دریافت کلاس سینگلتون، حتی بعد از اینکه اجازه دسترسی به ایجاد را محدود کرده‌ایم، باید از هرکلاسی در دسترس باشد. این مورد در کد جاوا از طریق متدهای static که مقدار instance از آبجکت را باز می‌گرداند قابل پیاده سازی بود. در زبان کاتلین نیز همانطور که در قطعه کد زیر می‌بینید می‌توان این مورد را میسر کرد.

در قطعه کد بالا با هربار فراخوانی getInstance() ، مقدار INSTANCE برای null بودن بررسی می‌شود و در صورتی که مقدار برابر null باشد یک نمونه از کلاس ایجاد و در این متغیر قرار می‌گیرد و در جواب متد getInstance()  بازگردانی می‌شود.

این نمونه از singleton شاید در بسیاری از موارد به درستی وظیفه خود را انجام دهد، اما در موارد دیگر ممکن است در صورت استفاده در محیطی با شرایط multi-thread منجر به ایجاد بیش از یک نمونه از سینگلتون شود.

در این قطعه کد، هرکدام از Threadهای اول و دوم نمونه‌های مجزایی از سینگلتون را ایجاد خواهند کرد. خب تقریبا به یک فاجعه برخورد کردیم، علاوه‌بر این که دیباگ کردن این کد می‌تواند خیلی سخت و یا حتی غیر ممکن باشد زیرا این مورد فقط در برخی شرایط اتفاق می‌افند و در بعضی شرایط به بهترین شکل ممکن عمل می‌کند. 😐

خب پس برای رفع این مشکل شاید بتوان صحبت بیشتر به سراغ محدودیتی از نوع synchronized برویم، پس قطعه کد بالا را بهشکل زیر اصلاح می‌کنیم:

کد بالا مشکل ایجاد چند نمونه از یک آبجکت را حل می‌کند اما می‌تواند منجر به مشکلات کارایی و ضعف در پرفورمنس شود! به این دلیل که قفل synchronized  بعد از اینکه برای ایجاد نمونه و instance گرفتن به بهترین شکل کار می‌کند و درسترسی به متد را به طور همزمان برای چند Thread محدود می‌سازد، پس از آن نیز اجازه درسترسی به مقدار نمونه در حالت multi-thread را نیز محدود می‌کند که به هیچ‌وجه نیازی به این محدودیت نیست! در حقیقت این محدودیت باعث کند شدن روند برنامه خواهد شد. اگر شما با این مورد مشکلی ندارید و یا مداوم به این سینگلتون مراجعه نمی‌کند، می‌توانید همینجا را پایان کار بدانید! و نیاز به خواندن مطلب بیشتری ندارید. اما اگر زیاد به این سینگلتون مراجعه می کنید و یا مثل من وسواس انجام کار به بهترین شکل را دارید، می‌توانیم با هم این مورد را نیز به بهینه ترین شکل تغییر دهیم. در قسمت بعدی این مطلب تمام مراحل بهینه کردن سینگلتون دوست‌داشتنی را روی زبان شیرین کاتلین قدم به قدم توضبح خواهم داد.

برچسب ها
نمایش بیشتر

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

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

بستن
بستن