JavaRush /وبلاگ جاوا /Random-FA /الگوهای طراحی: Singleton

الگوهای طراحی: Singleton

در گروه منتشر شد
سلام! امروز نگاهی دقیق تر به الگوهای مختلف طراحی خواهیم داشت و با الگوی Singleton شروع می کنیم که به آن "Singleton" نیز می گویند. الگوهای طراحی: Singleton - 1بیایید به یاد داشته باشیم: به طور کلی در مورد الگوهای طراحی چه می دانیم؟ الگوهای طراحی بهترین شیوه هایی هستند که می توان برای حل تعدادی از مشکلات شناخته شده دنبال کرد. الگوهای طراحی به طور کلی به هیچ زبان برنامه نویسی گره نمی خورند. آنها را به عنوان مجموعه ای از توصیه ها در نظر بگیرید، به دنبال آنها می توانید از اشتباهات جلوگیری کنید و چرخ خود را دوباره اختراع نکنید.

تک قلو چیست؟

تک‌تنه یکی از ساده‌ترین الگوهای طراحی است که می‌توان برای یک کلاس اعمال کرد. مردم گاهی اوقات می گویند "این کلاس تک تن است"، به این معنی که این کلاس الگوی طراحی تک تن را اجرا می کند. گاهی اوقات لازم است کلاسی بنویسیم که فقط یک شیء برای آن ایجاد شود. به عنوان مثال، کلاسی که مسئول ورود به سیستم یا اتصال به پایگاه داده است. الگوی طراحی Singleton توضیح می دهد که چگونه می توانیم چنین کاری را انجام دهیم. تک تون یک الگوی طراحی است که دو کار را انجام می دهد:
  1. تضمین می کند که یک کلاس فقط یک نمونه از کلاس را داشته باشد.

  2. یک نقطه دسترسی جهانی به نمونه ای از این کلاس ارائه می دهد.

از این رو، دو ویژگی وجود دارد که تقریباً برای هر اجرای الگوی تک‌تن مشخص است:
  1. سازنده خصوصی توانایی ایجاد اشیاء کلاس خارج از خود کلاس را محدود می کند.

  2. یک متد استاتیک عمومی که نمونه ای از کلاس را برمی گرداند. این روش نامیده می شود 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 بدون مشکل ایجاد شود.

مزایا و معایب الگوی سینگلتون

به طور کلی، تک تن دقیقاً همان کاری را انجام می دهد که از آن انتظار می رود:
  1. تضمین می کند که یک کلاس فقط یک نمونه از کلاس را داشته باشد.

  2. یک نقطه دسترسی جهانی به نمونه ای از این کلاس ارائه می دهد.

با این حال، این الگو دارای معایبی است:
  1. Singleton SRP (اصل مسئولیت واحد) را نقض می کند - کلاس Singleton علاوه بر مسئولیت های فوری، تعداد نسخه های آن را نیز کنترل می کند.

  2. وابستگی یک کلاس یا متد معمولی به تکی در قرارداد عمومی کلاس قابل مشاهده نیست.

  3. متغیرهای جهانی بد هستند. Singleton در نهایت به یک متغیر جهانی سنگین تبدیل می شود.

  4. وجود سینگلتون باعث کاهش تست پذیری اپلیکیشن به طور کلی و کلاس هایی می شود که از سینگلتون استفاده می کنند.

باشه الان تموم شد ما به الگوی طراحی تک تن نگاه کردیم. اکنون، در یک گفتگوی مادام العمر با دوستان برنامه نویس خود، می توانید نه تنها خوبی های آن را بیان کنید، بلکه چند کلمه در مورد بدی های آن نیز بگویید. موفق باشید در تسلط بر دانش جدید.

خواندن تکمیلی:

نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION