Seyed Morteza Kamali
Seyed Morteza Kamali
خواندن ۳ دقیقه·۵ سال پیش

معماری Scriptable Object بخش دوم - مشکلات Singleton

در این پست قصد داریم مشکلات Singleton را بررسی کنیم.

تعریف Singleton

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

کاربرد: بعضی کلاس ها هست که فقط باید یک نمونه از اونا باشه مثل GameManager یا SoundManager دیزاین پترن Singleton این تضمین رو میده که فقط و فقط یک نمونه از آنها باشه.


مزایای Singleton

همه جا می تونیم بهش دسترسی پیدا کنیم مثل:

PlayerManager.Instance.Name

حالت ماندگار:

همون طور که می دونید Singleton یک Instance هست که به صورت DontDestroy استفاده میشه و حتی اگر Scene هم عوض شود آن نابود نمی شود!

درک آسان:

این الگو خیلی راحت و قابل فهم است و به راحتی می شود از آن استفاده کرد

الگوی ثابت قدم:

از این الگو می توانیم در همه پروژه هایمان استفاده کنیم و کارمان را راحت می کند

برنامه ریزی آسان:

خیلی راحت می توانیم برنامه بریزیم که مثلاً یک PlayerManager یا EnemyManager داشته باشیم

مشکلات Singleton

اتصالات سفت و سخت:

ماژولار نیست و به قسمت های دیگه وابسته است یعنی نمی شود آن کلاس به تنهایی کار کند

بدون پلی مورفیسم:

از شئ گرایی پیروی نمی کند و نمی توان Polymorphism داشت تا رفتار های متفاوت برای آن تعریف کرد.

به خاطر همین شما ناچارید که کلاس های جداگانه تعریف کنید مثل

PlayerManager

TutorialPlayerManager

GameModeA_PlayerManager

GameModeB_PlayerManager

غیر قابل اشکال زدایی:

از آنجایی که نمی توان وظایف را Inject کرد و کلاس Manager بزرگ می شود تست کردن آن هم سخت می شود.

کابوس وابستگی:

بدی Singleton این است که در همه Scene ها هست و شاید اصلاً داخل یک Scene نیازی به آن نباشد.

در نتیجه ناچاریم آن را همه جا به یدک بکشیم که همین باعث می شود که مرحله اشکال زدایی سخت تر انجام شود.

وجود یک نمونه:

بدی Singleton این است که یک نمونه بیشتر از آن نمی شود داشته باشیم

به عنوان مثال در بازی یک نفره یک PlayerManager دارید

ولی در بازی چندنفره نمی توانید برای هر Player یک PlayerManager داشته باشید

در نتیجه ناچارید آرایه ای از Player ها را در کلاس PlayerManager داشته باشید که این روش جالبی نیست

راه حل ها:

کاهش استفاده از global manager ها

وارونگی کنترل:

مثل تزریق وابستگی :

یعنی اشیاء چیز هایی که نیاز دارند را مستقیم بگیرند نه اینکه وابسته به چیز دیگری باشند به عنوان مثال یک UI که قرار است نام بازیکن را نمایش دهد لزومی ندارد به کلاس PlayerManager وابسته شود و بنویسیم PlayerManager.Instance.Name می توان از Scriptable Object استفاده کرد تا Data بین آنها به اشتراک گذاشته شود.

اصل Single Responsibility:

هر کلاس یا کمپوننت شما یک کار را انجام دهد.

اطلاعات ماژولار

مثال Enemy Prefab

فرض کنید شما یک دشمن در بازی دارید که در حالت عادی می توان سرعت دشمن را از EnemyManager گرفت ولی فرض کنید 20 تا دشمن دارید که هر کدام سرعت مختلفی دارند.

این کار باعث می شود شما یک Hard Reference انجام دهید و Prefab دشمن را وابسته به EnemyManager کنید یعنی اگر این کلاس Load نشود یا به هر دلیلی از بین برود بازی به فنا می رود.

حالا هر چقدر پروژه بزرگ تر شود گند این Singeton مشخص می شود

به عنوان مثال

Enemy can't find EnemyManager

Player can't find PlayerManager

EnemyManager can't find PlayerManager

PlayerManager can't find Inventory Manager

راه حل چیست؟

همه آمارهای دشمن را با هم یکجا جمع کنیم؟

افزونه ها و ماژولار بودن را محدود کنیم؟

کدام بهتر است؟

singletonیونیتیبرنامه نویسیسی شارپ
شاید از این پست‌ها خوشتان بیاید