در این پست قصد داریم مشکلات Singleton را بررسی کنیم.
سینگلتون به شما اطمینان میدهد از یک کلاس، تنها یک نمونه یا شی (Object) ساخته شود. بنابراین در هر زمان که استفاده از این کلاس نیاز شود، سینگلتون یک دسترسی سراسری به تنها شیای که از این کلاس ساخته شده است ایجاد میکند و اجازه ساخت شی جدیدی را از آن کلاس نمیدهد.
کاربرد: بعضی کلاس ها هست که فقط باید یک نمونه از اونا باشه مثل GameManager یا SoundManager دیزاین پترن Singleton این تضمین رو میده که فقط و فقط یک نمونه از آنها باشه.
PlayerManager.Instance.Name
همون طور که می دونید Singleton یک Instance هست که به صورت DontDestroy استفاده میشه و حتی اگر Scene هم عوض شود آن نابود نمی شود!
این الگو خیلی راحت و قابل فهم است و به راحتی می شود از آن استفاده کرد
از این الگو می توانیم در همه پروژه هایمان استفاده کنیم و کارمان را راحت می کند
خیلی راحت می توانیم برنامه بریزیم که مثلاً یک PlayerManager یا EnemyManager داشته باشیم
ماژولار نیست و به قسمت های دیگه وابسته است یعنی نمی شود آن کلاس به تنهایی کار کند
از شئ گرایی پیروی نمی کند و نمی توان 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:
هر کلاس یا کمپوننت شما یک کار را انجام دهد.
فرض کنید شما یک دشمن در بازی دارید که در حالت عادی می توان سرعت دشمن را از 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
همه آمارهای دشمن را با هم یکجا جمع کنیم؟
افزونه ها و ماژولار بودن را محدود کنیم؟
کدام بهتر است؟