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