مروری جامع بر الگوی طراحی (Design Pattern) Singleton در سوئیفت (Swift)
الگوی Singleton یکی از سادهترین و پرکاربردترین الگوهای طراحی در برنامهنویسی است. این الگو به ما کمک میکند تا مطمئن شویم یک کلاس تنها یک نمونه (Instance) دارد و تمام برنامه به همان نمونه دسترسی دارد. اگر بخواهیم از این الگو استفاده کنیم، کلاس مورد نظر فقط یک بار میتواند ساخته شود و هر جایی که لازم باشد، همون نمونه (Instance) اولیه رو برمیگردونه.
پیادهسازی Singleton در سویفت
فرض کنید یک کلاس به نام Logger داریم که مسئول نوشتن لاگها (یعنی گزارشها) در یک فایل است. ما میخواهیم مطمئن باشیم که فقط یک Logger داریم که همه لاگها را مدیریت میکند.
بررسی کد:
تعریف نمونه ثابت: ما یک متغیر استاتیک به نام shared داخل کلاس تعریف کردیم. این متغیر همان نمونه Singleton است که با استفاده از کلمه کلیدی static به همه قسمتهای برنامه دسترسی دارد.
خصوصی کردن initializer: با خصوصی کردن initializer (private init())، جلوی ساخت نمونههای جدید از این کلاس را گرفتیم. یعنی هیچکس نمیتواند از این کلاس نمونه جدید بسازد و باید از همان نمونه shared استفاده کند.
متد برای نوشتن لاگها: این متد به سادگی یک پیام را میگیرد و آن را در کنسول چاپ میکند. فرض کنید به جای چاپ کردن، این پیامها در یک فایل ذخیره شود.
استفاده از Singleton
حالا که Logger ما آماده است، ببینیم چطور از آن استفاده کنیم:
هر وقت بخواهید یک لاگ بنویسید، فقط از Logger.shared استفاده میکنید. نکته مهم این است که هر جا در برنامهتان از Logger.sharedاستفاده کنید، همان یک نمونه Logger مورد استفاده قرار میگیرد.
چرا از Singleton استفاده کنیم؟
از Singleton استفاده میکنیم وقتی که:
منبعی محدود داریم: وقتی یک منبع محدود وجود دارد که میخواهیم تمام برنامه فقط به آن دسترسی داشته باشد، مثل دسترسی به یک دیتابیس یا یک تنظیمات مشترک.
کنترل مرکزی نیاز داریم: اگر نیاز باشد که یک کنترل مرکزی برای دسترسی به یک منبع یا یک بخش از برنامه داشته باشیم، Singleton راهحل خوبی است.
صرفهجویی در منابع: وقتی که ایجاد و نگهداری چندین نمونه از یک کلاس پرهزینه است و میخواهیم فقط یک بار آن را ایجاد کنیم.
چالشهای Singleton
با وجود اینکه Singleton خیلی مفید است، اما چالشهایی هم دارد:
مشکل در تستها (Unit Testing): یکی از بزرگترین چالشهای Singleton این است که چون همیشه همان یک نمونه از کلاس وجود دارد، تست کردنش خیلی سخت میشود.
وابستگیهای پنهان (Hidden Dependencies): Singletonها معمولاً وابستگیهای پنهان در کد ایجاد میکنند، که باعث میشود کد سختتر قابل نگهداری باشد.
مسائل مرتبط با Multi-threading: اگر Singleton در محیطهای چند ریسمانی (Multi-threaded) استفاده شود، باید مطمئن شویم که در مواقعی که چندین رشته به صورت همزمان به Singleton دسترسی دارند، مشکلاتی مثل دسترسی همزمان یا مشکلات همزمانی پیش نیاید.
جایگزینهای Singleton
بله، Singleton جایگزینهایی هم دارد:
جایگزینی به نام Dependency Injection (DI): به جای استفاده از Singleton، میتوانیم از الگوی Dependency Injection استفاده کنیم. این کار کمک میکند تا وابستگیها آشکارتر شوند و همچنین کد راحتتر تست شود.
جایگزین دیگری به نام Service Locator: این الگو شبیه به Singleton است اما انعطافپذیرتر. به جای اینکه خود کلاس Singleton را مستقیم فراخوانی کنیم، از یک سرویس واسط استفاده میکنیم که مسئول پیدا کردن و ارائه منابع مورد نیاز باشد.
چرا جایگزینها را انتخاب نمیکنیم؟
اگرچه جایگزینهایی مثل DI و Service Locator مزایای خودشان را دارند، اما هر کدام چالشهای خودشان را هم دارند:
پیچیدگی بیشتر: پیادهسازی DI یا Service Locator نسبت به Singleton پیچیدهتر است و نیاز به تنظیمات بیشتری دارد.
نیاز به پیکربندیهای بیشتر: پیادهسازی این الگوها نیاز به پیکربندیها و زیرساختهای خاصی دارد که ممکن است برای پروژههای کوچک بیش از حد پیچیده باشد.