Singleton پترن در JavaScript
سلام امروز اومدم که واستون یک دیزاین پترن رو توضیح بدم که اسم این دیزاین پترن Singleton هست
منطق کلی این دیزاین پترن میگه که ما باید class هایی داشته باشیم که فقط یکبار ازشون یک شی ایجاد کنیم و از این به بعد از اون بتونیم از اون شی ایجاد شده یکسره استفاده کنیم و دیگه شی جدیدی از کلاس نسازیم.
حالا که منطق کلیش رو فهمیدیم بریم که با یک مثال قشنگ درک کنیمش ( اگه منطق کلیش رو هم نفهمیدید مشکلی نیست مثال رو ببینید کاملا میفهمید منظورم چیه :)) )
اجزای کد ما :
- یک متغیر
counter
که توش مقدار شمارنده مون رو ذخیره میکنیم - یک متد
getInstance
که class ای که داریم روreturn
میکنه - یک متد
getCount
که مقدار فعلی متغیرcounter
روreturn
میکنه - یک متد
increment
که مقدار متغیرcounter
رو افزایش میده - یک متد
decrement
که مقدار متغیرcounter
رو کاهش میده
حتما براتون این سوال پیش میاد که خوب این که یک کلاس سادست! بله کاملا دارید درست میگید این کلاس ما هنوز معیار های دیزاین پترن Singleton رو نداره! چرا معیارش رو نداره؟ الان توضیح میدم :)
از یک کلاس Singleton فقط و فقط باید بشه یک شی ساخت و سری های بعدی باید از همون شی استفاده کرد اما از این کلاس ما میشه تا دلمون میخواد شی بسازیم :))
شاید بپرسید اون console.log
ته کد چیه که با هم مقایسشون کردی؟ نکته اینه که وقتی کلاسمون Singleton باشه خروجی های متد getInstance
اش با هم برابرند چون به یک کلاس اشاره میکنند اما اینجا چون هنوز کلاسمون Singleton نیست مقدارهاشون برابر نیستند
خوب حالا چطوری اینو بیاییم Singleton کنیمش؟
رسیدیم به جای قشنگش :)
بهترین راه برای اینکه مطمئن بشیم که فقط و فقط یک شی از این کلاسمون درست میشه اینه که یک متغیر بسازیم به اسم instance
و توی متد constructor
کلاس Counter
مون برای اولین باری که شی میخواد ساخته بشه کلاس رو تو متغیر instance بریزیم و در سری های بعدی ایجاد شدن شی مقدار متغیر instance رو چک کنیم و اگه مقدار متغیر خالی نبود ارور بدیم و بگیم فقط یکبار میشه از این کلاس شی ساخت
توضیحش سخت بود و متوجه نشدید؟ بریم که یک مثال بزنم تا قشنگ درکش کنید :)
و تماممممم :)) بلاخره تونستیم 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
اومده
وقتی ما متد 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 ای ندارن استیت رو آپدیت میکنن.
ممنون که وقتتون رو گذاشتید و این مقاله رو خوندید من این مقاله رو این شکلی نوشتم که اول این سایت رو خوندم و هرچی که فهمیدم رو براتون نوشتم و قراره مقاله های دیگه این وبسایت رو هم همین کار بکنم و واستون بزارمشون.
اگر اشکالی دیدید یا انتقادی داشتید ازم ممنون میشم تو کامنت ها بگیدش
مطلبی دیگر از این انتشارات
نحوه تست قراردادهای هوشمند (smart Contracts) اتریوم
مطلبی دیگر از این انتشارات
پشتوانه استیبلکوین LUNA تقویت میشود؛ بنیاد Terra جایگاه دومین هولدر بزرگ سازمانی بیتکوین را از Tesla ربود!
مطلبی دیگر از این انتشارات
ویتالیک بوترین: مردی که اتریوم را ایجاد کرد