سلام دوستان. گاهی اوقات توی برنامه کلاس‌هایی داریم که نمونه‌سازی از اونها هزینه زیادی داره. برای مثال کلاس کار با دیتابیس رو در نظر بگیرید:

class DB {
  private conn;

  constructor() {
    this.conn = mysql.createConnection('...')
  }

  public query() { ... }
}

// User.file
const db = new DB();
const users = db.query('...');

// Post.file
const db = new DB();
const posts = db.query('...');

طبق این کد، به محض ساخته‌شدن نمونه از کلاس DB، ارتباط با دیتابیس برقرار میشه. حالا اگه توی یک چرخه اجرای برنامه ۵۰ درخواست SQL داشته باشیم، ۵۰ تا کانکشن ساخته میشه که اصلاً بهینه نیست و باعث افت سرعت و مصرف بالای منابع سخت‌افزاری میشه.

یک راه برای حل این موضوع اینه که صرف نظر از اینکه توی برنامه چند تا درخواست SQL داریم (۵۰ تا؟ ۵۰۰ تا؟) کاری کنیم که توی سرتاسر برنامه فقط و فقط یک کانکشن وجود داشته باشه و کوئری‌های SQL رو مجبور کنیم که فقط از همون یک کانکشن استفاده کنن. که الگوی Singleton (سینگِلْتون) بهمون کمک می‌کنه تا این راه حل رو پیاده‌سازی کنیم 😉

 

الگوی Singleton چیه؟ 🤔

این الگو به ما این اطمینان رو میده که فقط یک نمونه از یک کلاس خاص در سرتاسر برنامه وجود داشته باشه و همچنین راهی رو ارائه میده تا بتونیم به همون نمونه دسترسی داشته باشیم.
این الگو که یکی از پراستفاده‌ترین و همچنین بحث‌برانگیز ترین الگوهاست، زیرمجموعه الگوهای Creational - الگوهایی روش‌های بهتری برای ساختن آبجکت‌ها ارائه میدن - هست و یکی از راحت‌ترین پیاده‌سازی‌ها رو داره. 

 

پیاده‌سازی الگوی Singleton

می‌خوایم این الگو رو با یک مثال از دنیای واقعی نرم‌افزارها پیاده‌سازی کنیم. همونطور که می‌دونیم برای هر برنامه‌ای یک‌سری تنظیمات خاص تعریف میشه. مثلاً تنظیمات مربوط به پیکربندی برنامه، دیتابیس، ایمیل و ... . برای مثال تنظیمات مربوط به دیتابیس رو توی فایلی به صورت زیر تعریف می‌کنیم:

// config/db.js
{
  'host': '127.0.0.1',
  'port': '3306',
  'user': 'root',
  'pass': 'secret'
};

و این فایل هم برای تنظیمات مربوط به ایمیل:

// config/mail.js
{
  'host': 'domain.com',
  'port': '587',
  'address': 'reply@domain.com'
};

حالا می‌خوایم کلاسی بنویسیم که بتونیم سرتاسر برنامه به این تنظیمات دسترسی داشته باشیم، اونها رو تغییر بدیم و یا مقدار جدیدی اضافه کنیم. کلاس Config رو در نظر بگیرید که هنوز هیچ الگویی روی اون اعمال نشده:

class Config {
  private items = [];

  constructor() {
    const files = this.loadAllConfigFiles();

    for (const file in files) {
      this.items[file] = files[file];
    }
  }

  public get(key) {
    return this.items[key];
  }

  public set(key, value) {
    this.items[key] = value;
  }
}