جلسه شماره 144رایگان

Singleton Design Pattern in Java | الگوی سینگلتون در جاوا

00:33:30

توضیحات جلسه

🎥 این ویدیو قسمت چهارم از پلی‌لیست تخصصی دوره آموزش دیزاین پترن‌ها در جاوا است. 📚 در این جلسه به سراغ یکی از محبوب‌ترین و پرکاربردترین الگوهای طراحی (Design Patterns) یعنی الگوی سینگلتون (Singleton) می‌رویم. در طول ویدیو ابتدا با مسئله و شرح الگوی سینگلتون آشنا می‌شویم، سپس با یک مثال ساده، ویژگی‌ها و اجزای آن را بررسی می‌کنیم. در ادامه مباحث مهمی مثل ایمنی در محیط‌های چندرشته‌ای (Thread-Safety) و روش‌های مختلف پیاده‌سازی از جمله Lazy Initialization، Synchronized، Eager Initialization، Double-Checked Locking، Static Inner Class و Enum Singleton را در عمل پیاده‌سازی و مقایسه می‌کنیم. در این جلسه یاد می‌گیریم: ⚠️ بررسی مشکلاتی که الگوی سینگلتون برای حل آن‌ها طراحی شده است 🧩 شرح ساختار، ویژگی‌ها و اجزای کلیدی (Components) الگوی Singleton 🧠 در پایان نیز یک جمع‌بندی کامل از نکات مهم جلسه خواهیم داشت اگر در حال یادگیری دیزاین پترن‌ها در جاوا هستید، تسلط بر جزئیات پیاده‌سازی الگوی سینگلتون و نکات ایمنی نخ‌ها (Thread Safety) از مهارت‌های ضروری یک برنامه‌نویس حرفه‌ای بک‌اند است. 📌 یوتیوب: youtube.com/@farzadafi 📌 آپارت: aparat.com/farzadafi 📌 وب‌سایت: https://farzadafi.ir 📌 گروه پرسش و پاسخ تلگرام: @programmingByFarzadAfi 💬 گروه پرسش و پاسخ بله: @programming_by_farzadafi وب سایت: https://farzadafi.ir هر سوالی داشته باشید می‌توانید در گروه بپرسید تا من یا مربی‌های گروه کمکتان کنیم 💬

جزوه و مستندات

مفاهیم کلیدی

  • Singleton Pattern

یک الگوی طراحی ساختاری از دسته الگوهای آفرینشی (Creational Design Patterns) که تضمین می‌کند یک کلاس در طول چرخه حیات برنامه فقط و فقط یک نمونه در حافظه (JVM) داشته باشد و یک نقطه دسترسی عمومی جهانی به آن ارائه دهد.

  • Private Constructor

سازنده‌ای با سطح دسترسی خصوصی که از نمونه‌سازی مستقیم کلاس با کلمه کلیدی new توسط سایر بخش‌های برنامه جلوگیری کرده و کنترل فرآیند ساخت شیء را کاملاً درون خود کلاس نگه می‌دارد.

  • Static Factory Method

متدی ایستا (معمولاً با نام getInstance()) که به عنوان دروازه جهانی عمل می‌کند و در هر فراخوانی، همان تک‌نمونه ساخته‌شده را از فیلد ایستا بازمی‌گرداند.

  • Lazy Initialization

رویکرد ساخت شیء در زمان نیاز (On-demand)؛ به این صورت که نمونه کلاس تا زمانی که متد getInstance() برای اولین بار فراخوانی نشود، در حافظه ایجاد نمی‌شود تا در مصرف منابع صرفه‌جویی گردد.

  • Eager Initialization

رویکرد ساخت نمونه در زمان بارگذاری اولیه کلاس (Class Loading Time) در JVM؛ این مدل به صورت پیش‌فرض ایمن در برابر چندنخی است اما ممکن است منابع را بدون استفاده نگه دارد.

  • Double-Checked Locking

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

  • Bill Pugh Singleton (Static Inner Class)

روشی بهینه در جاوا برای پیاده‌سازی سینگلتون که بر مکانیزم بارگذاری کلاس توسط JVM متکی است؛ کلاس درونی استاتیک تنها زمانی در حافظه لود می‌شود که متد getInstance() صدا زده شود، بنابراین بدون نیاز به همگام‌سازی صریح، رفتاری تنبل و کاملاً امن در برابر چندنخی ارائه می‌دهد.

  • Enum Singleton

ساده‌ترین و امن‌ترین شیوه پیاده‌سازی سینگلتون در جاوا بر اساس توصیه جاشوا بلوچ؛ نوع شمارشی به صورت ذاتی توسط JVM مدیریت می‌شود و در برابر حملات بازتابی (Reflection) و خطاهای سریال‌سازی (Serialization) مقاوم است.

موارد مصاحبه ای

  • چرا متد سازنده در الگوی Singleton باید خصوصی (private) باشد؟

سازنده‌های عمومی به هر کدی اجازه می‌دهند با دستور new نمونه‌های جدیدی بسازد؛ خصوصی کردن سازنده این امکان را مسدود می‌کند و تضمین می‌دهد که کنترل نمونه‌سازی منحصراً در اختیار خود کلاس باشد.

  • مشکل پیاده‌سازی کلاسیک و ساده Singleton در محیط چندنخی (Multithreading) چیست؟

در پیاده‌سازی کلاسیک تنبل، اگر دو نخ به صورت همزمان شرط instance == null را بررسی کنند، هر دو شرط را برقرار دیده و دو نمونه مجزا تولید می‌کنند که منجر به پدیده رقابت فاجعه‌بار (Race Condition) و شکستن قاعده تک‌نمونه بودن می‌شود.

  • مزیت Double-Checked Locking نسبت به متد کاملاً synchronized چیست؟

افزودن کلمه کلیدی synchronized به امضای متد getInstance() باعث قفل شدن تمام فراخوانی‌ها در طول برنامه و افت شدید پرفورمنس می‌شود؛ در حالی که Double-Checked Locking بررسی اولیه نال بودن را بیرون از بلوک همگام‌سازی انجام می‌دهد و قفل فقط برای اولین بار ایجاد نمونه فعال می‌گردد.

  • چرا Enum به عنوان امن‌ترین روش پیاده‌سازی Singleton در جاوا شناخته می‌شود؟

مقادیر Enum توسط JVM به صورت تضمین‌شده دقیقاً یک‌بار نمونه‌سازی می‌شوند و جاوا تضمین ساختاری دارد که هیچ کدی حتی از طریق بازتاب یا عملیات دی‌سریال‌سازی نمی‌تواند نمونه جدیدی از یک Enum ایجاد کند.

  • معایب و محدودیت‌های اصلی استفاده از الگوی Singleton چیست؟

ایجاد وابستگی شدید (Tight Coupling) بین کلاس‌ها، دشواری در آزمون‌پذیری (Unit Testing) به دلیل وجود وضعیت مشترک سراسری، و ایجاد رفتارهای پیش‌بینی‌نشده در سناریوهای همزمانی در صورت عدم مدیریت درست ایمنی نخ‌ها.

سناریو کاربردی

  • مدیریت متمرکز اتصال پایگاه داده و بارگذاری تنبل از طریق کلاس درونی

در سناریوهایی نظیر ایجاد استخر اتصال پایگاه داده (Database Connection Manager)، لاگین برنامه‌ها یا بارگذاری تنظیمات که ساخت چندین نمونه هزینه بالایی دارد، الگوی سینگلتون تضمین می‌کند که تمامی ماژول‌ها از یک منبع مشترک استفاده کنند:

public class DatabaseConnectionManager {

    private DatabaseConnectionManager() {
        // پیکربندی اولیه و اتصال به پایگاه داده
    }

    private static class Holder {
        private static final DatabaseConnectionManager INSTANCE = new DatabaseConnectionManager();
    }

    public static DatabaseConnectionManager getInstance() {
        return Holder.INSTANCE;
    }

    public void executeQuery(String sql) {
        // اجرای عملیات روی اتصال مشترک
    }
}

مراحل کار: ۱. مسدودسازی ساخت نمونه خارجی با تعریف سازنده private. ۲. به تعویق انداختن بارگذاری کلاس کمکی Holder تا زمان اولین فراخوانی getInstance(). ۳. تضمین ساخت دقیقاً یک نمونه امن و غیرقابل تغییر در سطح ماشین مجازی جاوا.

بیشتر بدانید

  • تضمین دیدپذیری با کلیدواژه volatile

در پیاده‌سازی Double-Checked Locking، فیلد نمونه باید با کلمه کلیدی volatile مشخص شود تا از تغییر ترتیب دستورات (Instruction Reordering) توسط کامپایلر یا سخت‌افزار جلوگیری شود و نخی نتواند به شیئی دسترسی پیدا کند که مقداردهی آن در حافظه هنوز کامل نشده است.

  • جلوگیری از ساخت نمونه از طریق Serialization

اگر کلاس سینگلتون اینترفیس Serializable را پیاده‌سازی کند، هنگام خواندن از جریان با متد readObject() نمونه جدیدی ساخته می‌شود؛ برای جلوگیری از این نقض قاعده، باید متد readResolve() در کلاس تعریف شده و همان فیلد تک‌نمونه بازگردانده شود.