تک قلو چیست؟
تکتنه یکی از سادهترین الگوهای طراحی است که میتوان برای یک کلاس اعمال کرد. مردم گاهی اوقات می گویند "این کلاس تک تن است"، به این معنی که این کلاس الگوی طراحی تک تن را اجرا می کند. گاهی اوقات لازم است کلاسی بنویسیم که فقط یک شیء برای آن ایجاد شود. به عنوان مثال، کلاسی که مسئول ورود به سیستم یا اتصال به پایگاه داده است. الگوی طراحی Singleton توضیح می دهد که چگونه می توانیم چنین کاری را انجام دهیم. تک تون یک الگوی طراحی است که دو کار را انجام می دهد:-
تضمین می کند که یک کلاس فقط یک نمونه از کلاس را داشته باشد.
-
یک نقطه دسترسی جهانی به نمونه ای از این کلاس ارائه می دهد.
-
سازنده خصوصی توانایی ایجاد اشیاء کلاس خارج از خود کلاس را محدود می کند.
-
یک متد استاتیک عمومی که نمونه ای از کلاس را برمی گرداند. این روش نامیده می شود
getInstance
. این نقطه دسترسی جهانی به نمونه کلاس است.
گزینه های پیاده سازی
الگوی طرح تک تن به روش های مختلفی استفاده می شود. هر گزینه در نوع خود خوب و بد است. اینجا، مثل همیشه: ایده آل وجود ندارد، اما باید برای آن تلاش کنید. اما قبل از هر چیز، اجازه دهید تعریف کنیم که چه چیزی خوب است و چه چیزی بد، و چه معیارهایی بر ارزیابی اجرای یک الگوی طراحی تاثیر میگذارند. بیایید با مثبت شروع کنیم. در اینجا معیارهایی وجود دارد که به اجرا آبادی و جذابیت می بخشد:-
مقداردهی اولیه تنبل: زمانی که یک کلاس بارگذاری می شود در حالی که برنامه دقیقاً در زمانی که نیاز است اجرا می شود.
-
سادگی و شفافیت کد: متریک، البته، ذهنی است، اما مهم است.
-
ایمنی نخ: در یک محیط چند رشته ای به درستی کار می کند.
-
عملکرد بالا در یک محیط چند رشته ای: رشته ها هنگام اشتراک گذاری یک منبع، یکدیگر را به حداقل ممکن مسدود می کنند یا اصلاً یکدیگر را مسدود نمی کنند.
-
مقداردهی اولیه غیر تنبل: هنگامی که یک کلاس هنگام شروع برنامه بارگیری می شود، صرف نظر از اینکه به آن نیاز است یا نه (یک پارادوکس، در دنیای IT بهتر است تنبل باشید)
-
پیچیدگی و خوانایی ضعیف کد. متریک نیز ذهنی است. فرض می کنیم که اگر خون از چشم بیاید، اجرا چنین است.
-
عدم ایمنی نخ به عبارت دیگر، "خطر نخ". عملکرد نادرست در یک محیط چند رشته ای.
-
عملکرد ضعیف در یک محیط چند رشته ای: رشته ها همیشه یا اغلب هنگام به اشتراک گذاری یک منبع یکدیگر را مسدود می کنند.
کد
اکنون ما آماده هستیم تا گزینه های مختلف پیاده سازی را در نظر بگیریم و مزایا و معایب را ذکر کنیم:راه حل ساده
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return INSTANCE;
}
}
ساده ترین پیاده سازی طرفداران:
-
سادگی و شفافیت کد
-
ایمنی نخ
-
عملکرد بالا در یک محیط چند رشته ای
- تنظیم اولیه تنبل نیست.
Lazy Initialization
public class Singleton {
private static Singleton INSTANCE;
private Singleton() {}
public static Singleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
طرفداران:
-
مقداردهی اولیه تنبل
-
نخ ایمن نیست
دسترسی همزمان
public class Singleton {
private static Singleton INSTANCE;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
طرفداران:
-
مقداردهی اولیه تنبل
-
ایمنی نخ
-
عملکرد ضعیف در یک محیط چند رشته ای
getInstance
همگام شده است، و شما فقط می توانید آن را یکی یکی وارد کنید. در واقع، ما نیازی به همگام سازی کل متد نداریم، بلکه تنها بخشی از آن را که در آن یک شی کلاس جدید را مقداردهی اولیه می کنیم، نیاز داریم. اما ما نمی توانیم به سادگی synchronized
بخشی که مسئول ایجاد یک شی جدید است را در یک بلوک بپیچانیم: این امر ایمنی نخ را تامین نمی کند. کمی پیچیده تر است. روش صحیح همگام سازی در زیر آورده شده است:
قفل دوبار چک شده
public class Singleton {
private static Singleton INSTANCE;
private Singleton() {
}
public static Singleton getInstance() {
if (INSTANCE == null) {
synchronized (Singleton.class) {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
طرفداران:
-
مقداردهی اولیه تنبل
-
ایمنی نخ
-
عملکرد بالا در یک محیط چند رشته ای
-
در نسخه های جاوا کمتر از 1.5 پشتیبانی نمی شود (کلید کلیدی فرار در نسخه 1.5 ثابت شده است)
INSTANCE
باید یا final
، یا باشد volatile
. آخرین اجرایی که امروز در مورد آن بحث خواهیم کرد این است Class Holder Singleton
.
دارنده کلاس سینگلتون
public class Singleton {
private Singleton() {
}
private static class SingletonHolder {
public static final Singleton HOLDER_INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.HOLDER_INSTANCE;
}
}
طرفداران:
-
مقداردهی اولیه تنبل
-
ایمنی نخ.
-
عملکرد بالا در یک محیط چند رشته ای.
-
برای عملکرد صحیح، لازم است تضمین شود که شی کلاس
Singleton
بدون خطا مقداردهی اولیه شده است. در غیر این صورت، فراخوانی روش اولgetInstance
با خطا به پایان می رسدExceptionInInitializerError
و همه موارد بعدی ناموفق خواهند بودNoClassDefFoundError
.
پیاده سازی | مقداردهی اولیه تنبل | ایمنی نخ | سرعت چند رشته ای | چه موقع باید استفاده کرد؟ |
---|---|---|---|---|
راه حل ساده | - | + | سریع | هرگز. یا زمانی که تنظیم اولیه تنبل مهم نیست. اما هرگز بهتر نیست. |
Lazy Initialization | + | - | قابل اجرا نیست | همیشه زمانی که نیازی به چند رشته ای نیست |
دسترسی همزمان | + | + | به آرامی | هرگز. یا زمانی که سرعت کار با multithreading مهم نیست. اما هرگز بهتر نیست |
قفل دوبار چک شده | + | + | سریع | در موارد نادری که هنگام ایجاد تکتنه نیاز به کنترل استثناها دارید. (زمانی که Class Holder Singleton قابل اجرا نیست) |
دارنده کلاس سینگلتون | + | + | سریع | همیشه زمانی که نیاز به multithreading است و تضمین وجود دارد که یک شی کلاس singleton بدون مشکل ایجاد شود. |
مزایا و معایب الگوی سینگلتون
به طور کلی، تک تن دقیقاً همان کاری را انجام می دهد که از آن انتظار می رود:-
تضمین می کند که یک کلاس فقط یک نمونه از کلاس را داشته باشد.
-
یک نقطه دسترسی جهانی به نمونه ای از این کلاس ارائه می دهد.
-
Singleton SRP (اصل مسئولیت واحد) را نقض می کند - کلاس Singleton علاوه بر مسئولیت های فوری، تعداد نسخه های آن را نیز کنترل می کند.
-
وابستگی یک کلاس یا متد معمولی به تکی در قرارداد عمومی کلاس قابل مشاهده نیست.
-
متغیرهای جهانی بد هستند. Singleton در نهایت به یک متغیر جهانی سنگین تبدیل می شود.
-
وجود سینگلتون باعث کاهش تست پذیری اپلیکیشن به طور کلی و کلاس هایی می شود که از سینگلتون استفاده می کنند.
GO TO FULL VERSION