‫Singleton پترن در JavaScript

Singleton پترن
Singleton پترن


سلام امروز اومدم که واستون یک دیزاین پترن رو توضیح بدم که اسم این دیزاین پترن Singleton هست

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

حالا که منطق کلیش رو فهمیدیم بریم که با یک مثال قشنگ درک کنیمش ( اگه منطق کلیش رو هم نفهمیدید مشکلی نیست مثال رو ببینید کاملا میفهمید منظورم چیه :)) )

اجزای کد ما :

  • یک متغیر counter که توش مقدار شمارنده مون رو ذخیره میکنیم
  • یک متدgetInstanceکه class ای که داریم رو return میکنه
  • یک متدgetCountکه مقدار فعلی متغیر counter رو return میکنه
  • یک متد increment که مقدار متغیرcounterرو افزایش میده
  • یک متد decrement که مقدار متغیر counter رو کاهش میده
کلاس Counter ما
کلاس Counter ما

حتما براتون این سوال پیش میاد که خوب این که یک کلاس سادست! بله کاملا دارید درست میگید این کلاس ما هنوز معیار های دیزاین پترن Singleton رو نداره! چرا معیارش رو نداره؟ الان توضیح میدم :)

از یک کلاس Singleton فقط و فقط باید بشه یک شی ساخت و سری های بعدی باید از همون شی استفاده کرد اما از این کلاس ما میشه تا دلمون میخواد شی بسازیم :))

Counter ساختن شی های مختلف از کلاس
Counter ساختن شی های مختلف از کلاس

شاید بپرسید اون console.log ته کد چیه که با هم مقایسشون کردی؟ نکته اینه که وقتی کلاسمون Singleton باشه خروجی های متد getInstance اش با هم برابرند چون به یک کلاس اشاره میکنند اما اینجا چون هنوز کلاسمون Singleton نیست مقدارهاشون برابر نیستند

Counter درست شدن دو شی مختلف از کلاس
Counter درست شدن دو شی مختلف از کلاس

خوب حالا چطوری اینو بیاییم Singleton کنیمش؟

رسیدیم به جای قشنگش :)

بهترین راه برای اینکه مطمئن بشیم که فقط و فقط یک شی از این کلاسمون درست میشه اینه که یک متغیر بسازیم به اسم ‫instance و توی متد constructor کلاس Counter مون برای اولین باری که شی میخواد ساخته بشه کلاس رو تو متغیر instance بریزیم و در سری های بعدی ایجاد شدن شی مقدار متغیر instance رو چک کنیم و اگه مقدار متغیر خالی نبود ارور بدیم و بگیم فقط یکبار میشه از این کلاس شی ساخت

توضیحش سخت بود و متوجه نشدید؟ بریم که یک مثال بزنم تا قشنگ درکش کنید :)

کلاس  Counter ما که singleton شده
کلاس Counter ما که singleton شده

و تماممممم :)) بلاخره تونستیم Singleton اش کنیم

الان اگه دوبار از کلاسمان شی بسازیم برنامه ارور میده

حالا بیاییم این کلاسمون رو بریزیم تو یه فایل به اسم از فایل counter.js و بعد export کنیمش اما یه نکته ریزی رو باید اینجا رعایت کنیم که شی ساخته شده رو بریزیم داخل Object.freeze که مقداری از آبجکتی که داریم قابل تغییر نباشه و به نوعی آبجکتمون یخ بزنه :)))

آبجکتی که داریم رو فریز کردیمش :)
آبجکتی که داریم رو فریز کردیمش :)

حالا بیاییم یه برنامه خیلی ساده با این پترن بسازیم که ببینیم چه شکلی میشه برناممون

  • ‫فایل counter.js که توی خودش کلاس Counter رو داره و در نهایت فریز شده اون رو export کردیم
  • فایل index.js که توی خودش ایمپورت میکنه فایل redButton.js و فایل blueButton.js رو
  • فایل redButton.js که توی خودش import کرده فایل counter.js رو و یک button داره که رنگش قرمزه و روی اون یک event کلیک ست شده که متدincrement رو صدا میزنه و بعدش متد getCount رو صدا میزنه و مقدار برگشتی اون رو console.log میکنه
  • ‫فایلblueButton.js دقیقا همان کار فایل redButton.js رو میکنه ولی رنگش آبیه :))) (عجب تفاوت بزرگی)

نکته ای که اینجا باید خیلی حواسمون باشه اینه که هر دو دکمه redButton و blueButton از یک instance استفاده میکنن که از counter.jsاومده

طوری که button هامون دارن از کلاس Counter استفاده میکنن
طوری که button هامون دارن از کلاس Counter استفاده میکنن

وقتی ما متد ‫increment رو در زمان کلیک redButton یا blueButton صدا میزنیم مقدار متغیر counter تو هر دوتا فایل فرق میکنه چون از این کلاس یک شی مشترک ساخته بودیم


اما استفاده از هر چیزی یه سری معایب هم داره که باید اون هارو هم دونست و بعد از اون چیز استفاده کرد پس بریم تا ببینیم معایب این کار چیه

نیازی به class نداریم !

یکی از خوبی هایی که ما توی این پترن داریم اینه که این پترن باعث میشه که تو استفاده از رممون صرفه جویی داشته باشیم چون دیگه هی لازم نیست شی های مختلف از یک کلاس بسازیم.

لابد الان میگید که خوب این که همش فایده بود که ضررش کو؟. الان میگم ضررش کجاست :

تو زبون های دیگه ای مثل Java و ++‫c این امکان وجود نداره که به صورت مستقیم ما یک آبجکت بسازیم و ازش استفاده کنیم اما توی js این امکان وجود داره و ما میتونیم خیلی کدمون رو ساده تر و راحت تر بنویسیمش بدون داشتن متغیر instance

الان حتما مثل خودم هیجان زده اید که یعنی چطوری میشه اینکارو کرد؟ بریم که مثال رو ببینیم با هم:

اجزای کد :

  • متغیر count که توش مقدار شمارنده مون رو ذخیره میکنیم
  • متدincrement که توش مقدار متغیر count رو یک واحد اضافه میکنیم
  • متد decrement که توش مقدار متغیر count رو یکم واحد کم میکنیم
  • متد getCount که توش مقدار متغیر count رو ریترن میکنیم
نوشتن یک آبجکت ساده که دقیقا چیزی که ما میخواییم رو داره
نوشتن یک آبجکت ساده که دقیقا چیزی که ما میخواییم رو داره

مشکل داشتن تو تست نویسی

اگه تو برنامتون تست داشته باشید و بخوایید این class رو واسش تست بنویسید باید حواستون باشه که بخاطر تعداد صدا زدن متد های مختلف یا بهم خوردن ترتیب صدا زدنشون مقدار تست های پایینی هم ممکنه چیزی که میخوایید نباشه و این شکلی میشه که همه تست هاتون fail میشه در صورتی که class تون داره درست کار میکنه :)))

نمونه یک تست نوشته شده
نمونه یک تست نوشته شده


معلوم نبودن مقدار فعلی

از اونجایی که این مقدار ما داره تو کل برناممون استفاده میشه ممکنه ما از این کلاس توی جایی استفاده کنیم و انتظار داشته باشیم که مقدارش مثلا 0 باشه اما یهو میبینیم چون تو یه کامپوننت دیگه مقدارش زیاد شده مقدار اولیه اش 4 هست و باعث میشه باگ های عجیب و غریب تو برنامه درست بشه


جمع بندی کلی :

در کل ایده داشتن متغیر هایی بصورت گلوبال ایده خوبی نیست چون میتونه اتفاقات غیر منتظره ای رو تو برنامه ما ایجاد بکنه مخصوصا تو es6 و این داستانا که let و const اومدن باعث شدن که دولپر ها تا حد ممکن متغیر هایی رو داشته باشن تو برنامه هاشون که پایبند یک scope باشه. اما بازم با سیستم ماژول بندی js میشه تا حدی از این اتفاقات غیر منتظره جلوگیری کرد.

مهم ترین چیز تو استفاده از این پترن ترتیبیه که دارید از این کلاس ها میکنید باید سعی کنید تا حد ممکن بصورت تصادفی داده هارو عوض نکنید و اون هارو زمانی استفاده کنید که هنوز داده ای برای مصرف وجود نداره (منظورم موقع ایجاد شدن یک کامپوننت و یا اتصال و گرفتن دیتا از دیتابیس و ایناست)

تو React ما اغلب به جای استفاده از کلاس های Singleton از ابزارهای State management مثل Redux یا React Context استفاده میکنیم. اگرچه گلوبال بودن اونها ممکنه شبیه به کلاس های Singleton باشه اما این ابزارها به جای حالت قابل تغییر دادن دیتا تو Singleton فقط امکان خوندن دیتا ها را ارائه میدن مثلا وقتی که داریم از Redux استفاده میکنیم تنها فانکشن های reducer که هیچ side effect ای ندارن استیت رو آپدیت میکنن.


ممنون که وقتتون رو گذاشتید و این مقاله رو خوندید من این مقاله رو این شکلی نوشتم که اول این سایت رو خوندم و هرچی که فهمیدم رو براتون نوشتم و قراره مقاله های دیگه این وبسایت رو هم همین کار بکنم و واستون بزارمشون.

اگر اشکالی دیدید یا انتقادی داشتید ازم ممنون میشم تو کامنت ها بگیدش