زمان تخمینی مطالعه: 7 دقیقه
الگوی Singleton را احتمالاً میتوان پرکاربردترین الگوی طراحی و یکی از اصلیترین زیر دستههای الگوهای طراحی ساختاری(Creational) دانست. الگوی Singleton به کاربر امکان میدهد اطمینان حاصل کند که یک کلاس فقط یک نمونه(instance) دارد، در حالی که یک نقطه دسترسی گلوبال به این نمونه ارائه میدهد. این الگو دارای یک ساختار ساده، آسان برای درک و استفاده میباشد. سادگی آن باعث شده که گاهی اوقات بیش از اندازه و در سناریوهایی که نیازی به آن نیست استفاده شود. در چنین مواردی، معایب استفاده از آن بیشتر از مزایایی است که به همراه دارد. به همین دلیل، گاهی اوقات الگوی Singleton را یک پادالگو(antipattern) یا الگوی تکی(pattern singleton) در نظر میگیرند.
بیان مسئله: الگوی Singleton دو مشکل را به طور همزمان حل میکند و اصل مسئولیت واحد(Single Responsibility Principle) را نقض میکند:
- اطمینان حاصل کنید که یک کلاس فقط یک نمونه دارد. چرا کسی باید بخواهد تعداد نمونههای یک کلاس را کنترل کند؟ رایج ترین دلیل این امر کنترل دسترسی به برخی از منابع مشترک است – به عنوان مثال، یک پایگاه داده یا یک فایل. نحوه کار به این صورت است که تصور کنید که یک شی ایجاد کردهاید، ولی پس از مدتی تصمیم به ایجاد یک شی جدید دارید. در این حالت به جای دریافت یک شیء جدید، شیای را که قبلا ایجاد کردهاید مجددا دریافت خواهید کرد. توجه داشته باشید که اجرای این رفتار با یک سازنده(Constructor) معمولی غیرممکن است زیرا یک فراخوانی سازنده همیشه باید یک شی جدید را بر طبق طراحی خود برگرداند.
- یک نقطه دسترسی سراسری(Global) به آن نمونه(نمونه تولیدی در مرحله اول) ارائه دهید. درست مانند یک متغیر سراسری، الگوی Singleton به شما امکان میدهد از هر کجای برنامه به برخی از شیها دسترسی داشته باشید. با این حال، آن نمونه تولید شده را از بازنویسی شدن توسط کدهای دیگر نیز محافظت میکند.
نکته: امروزه الگوی Singleton آنقدر محبوب شده است که مردم ممکن است چیزی را Singleton بنامند حتی اگر فقط یکی از مشکلات ذکر شده را حل کند.
در حالت کلی تمام پیادهسازیهای الگوی Singleton دارای دو مرحله مشترک هستند:
- سازنده پیش فرض را خصوصی(Private) کنید تا دیگر اشیا از عملگر new با کلاس Singleton استفاده نکنند.
- یک متد ایجاد ایستا(Static) ایجاد کنید که به عنوان سازنده عمل میکند. در عین حال، این روش سازنده خصوصی را برای ایجاد یک شی فراخوانی میکند و آن را در یک فیلد ثابت ذخیره میکند. همه فراخوانیهای بعدی به این متد، شیء ذخیره شده را برمیگرداند.
نکته: اگر کد شما به کلاس Singleton دسترسی دارد، میتواند متد Singleton را فراخوانی کند. بنابراین هر زمان که آن متد فراخوانی شود، همیشه همان شی برگردانده میشود.
نمونه قابل قیاس در دنیای واقعی
دولت هر کشور نمونهای عالی از الگوی Singleton است. یک کشور میتواند تنها یک دولت رسمی داشته باشد. صرف نظر از هویت شخصی افراد تشکیل دهنده دولت، عنوان “دولت X” یک نقطه دسترسی جهانی است که گروهی از افراد مسئول را مشخص میکند.
ساختار الگوی Singleton
در این بخش به بررسی ساختار الگوی سینگلتون میپردازیم و پیادهسازی آن را به صورت UML خواهیم دید.
کلاس Singleton متد static با نام getInstance را اعلام میکند که همان نمونه کلاس خود را برمیگرداند. بعلاوه سازنده Singleton باید از کد مشتری پنهان باشد و فراخوانی متد getInstance باید تنها راه دریافت شی Singleton باشد.
شبه کد(Pseudocode)
در این نمونه کد، کلاس اتصال پایگاه داده به عنوان Singleton عمل میکند. این کلاس سازنده عمومی ندارد، بنابراین تنها راه برای دریافت شیء آن فراخوانی متد getInstance است. این متد اولین شی ایجاد شده را گرفته و در تمام فراخوانیهای بعدی آن را بر میگرداند.
// The Database class defines the `getInstance` method that lets
// clients access the same instance of a database connection
// throughout the program.
class Database is
// The field for storing the singleton instance should be
// declared static.
private static field instance: Database
// The singleton's constructor should always be private to
// prevent direct construction calls with the `new`
// operator.
private constructor Database() is
// Some initialization code, such as the actual
// connection to a database server.
// ...
// The static method that controls access to the singleton
// instance.
public static method getInstance() is
if (Database.instance == null) then
acquireThreadLock() and then
// Ensure that the instance hasn't yet been
// initialized by another thread while this one
// has been waiting for the lock's release.
if (Database.instance == null) then
Database.instance = new Database()
return Database.instance
// Finally, any singleton should define some business logic
// which can be executed on its instance.
public method query(sql) is
// For instance, all database queries of an app go
// through this method. Therefore, you can place
// throttling or caching logic here.
// ...
class Application is
method main() is
Database foo = Database.getInstance()
foo.query("SELECT ...")
// ...
Database bar = Database.getInstance()
bar.query("SELECT ...")
// The variable `bar` will contain the same object as
// the variable `foo`.
قابلیتها و کاربردها
- از الگوی Singleton زمانی استفاده کنید که یک کلاس در برنامه شما باید فقط یک نمونه در دسترس برای همه مشتریان داشته باشد. به عنوان مثال، یک شی پایگاه داده واحد را در نظر بگیرید که توسط بخشهای مختلف برنامه به اشتراک گذاشته شده است. الگوی Singleton تمام ابزارهای دیگر برای ایجاد اشیاء یک کلاس را غیرفعال میکند، به جز روش ایجاد خاص. این روش یا یک شی جدید ایجاد میکند یا اگر قبلا ایجاد شده باشد،آن را برمیگرداند.
- زمانی که به کنترل دقیقتری روی متغیرهای سراسری نیاز دارید از الگوی Singleton استفاده کنید. برخلاف متغیرهای سراسری، الگوی Singleton تضمین میکند که فقط یک نمونه از یک کلاس وجود دارد. هیچ چیز، به جز خود کلاس Singleton، نمیتواند جایگزین نمونه ذخیره شده شود.
نکته: همیشه میتوانید این محدودیت را تنظیم کنید و اجازه ایجاد هر تعداد نمونه Singleton را بدهید. تنها کدی که نیاز به تغییر دارد، بدنه متد getInstance است.
نحوه پیادهسازی
- یک فیلد استاتیک خصوصی(private static) به کلاس برای ذخیره نمونه singleton اضافه کنید.
- یک روش ایجاد استاتیک عمومی برای به دست آوردن نمونه Singleton اعلام کنید.
- در داخل متد استاتیک، “مقدار دهی اولیه تنبل(lazy initialization)” را پیادهسازی کنید. این متد باید در اولین فراخوانی، یک شی جدید ایجاد کرده و آن را در فیلد استاتیک قرار دهد. متد همیشه باید آن نمونه را در تمام فراخوانیهای بعدی برگرداند.
- سازنده کلاس را خصوصی کنید. متد استاتیک کلاس همچنان قادر خواهد بود سازنده را فراخوانی کند، اما سایر اشیاء را نه.
- روی کد کلاینت بروید و همه فراخوانیهای مستقیم سازنده singleton را با فراخوانیهای روش ایجاد استاتیک آن جایگزین کنید.
مزایا و معایب الگوی Singleton
این الگوی طراحی دارای مزایا و معایبی به شرح زیر است:
– مزایا
- می توانید مطمئن باشید که یک کلاس فقط یک نمونه دارد.
- داشتن فقط یک نقطه دسترسی سراسری به آن نمونه ایجاد شده.
- شی Singleton فقط زمانی مقداردهی اولیه میشود که برای اولین بار درخواست شده باشد.
– معایب
- اصل مسئولیت واحد را نقض میکند. این الگو به طور همزمان دو مشکل را حل میکند.
- الگوی Singleton میتواند طراحی بد را بپوشاند، به عنوان مثال، زمانی که اجزای برنامه اطلاعات زیادی در مورد یکدیگر دارند.
- این الگو نیاز به مراقبت خاص در محیط چند رشتهای(multithreaded) دارد به طوری که نخهای متعدد چندین بار یک شی singleton را ایجاد نکنند.
- ممکن است یونیت تست کد کلاینت Singleton دشوار باشد زیرا بسیاری از چارچوبهای تست در هنگام تولید اشیاء ساختگی(mock objects) به وراثت متکی هستند. از آنجایی که سازنده کلاس singleton خصوصی است و غلبه بر روشهای استاتیک در بیشتر زبانها غیرممکن است، باید راهی خلاقانه برای تقلید کردن از singleton بیاندیشید.
ارتباط با الگوهای دیگر
- یک کلاس Facade اغلب میتواند به یک Singleton تبدیل شود زیرا یک شی Facade در بیشتر موارد کافی است.
- الگوی Flyweight شبیه الگوی Singleton خواهد بود اگر به نحوی بتوانید تمام حالات مشترک اشیاء را به یک شی Flyweight کاهش دهید. اما دو تفاوت اساسی بین این الگوها وجود دارد:
- فقط یک نمونه Singleton باید وجود داشته باشد، در حالی که یک کلاس Flyweight میتواند چندین نمونه با حالتهای ذاتی مختلف داشته باشد.
- شی Singleton میتواند قابل تغییر باشد. اشیاء Flyweight تغییرناپذیر هستند.
- Abstract Factory، Prototype یا Builder همگی میتوانند بهعنوان Singletons پیادهسازی شوند.