یکی دیگر از ویژگی های جالب برنامه نویسی شیء گرا، امکان وجود private constructor است. یکی از الگوهای موجود در برنامه نویسی شیء گرا، الگوی singleton است. شما با استفاده از این الگوی کدنویسی مطمئن خواهید شد که در هر زمان فقط یک نمونه از هر کلاس را دارید. این الگو معمولا زمانی کاربردی است که نمی توانید یا نمی خواهید از متدها و خصوصیات استاتیک استفاده کنید اما در عین حال می خواهید مطمئن شوید که از هر کلاس فقط یک نمونه وجود دارد.
مثلا در کدهای ما می توان گفت فقط یک دپارتمان حسابداری (accounting) وجود دارد، بنابراین می خواهیم همیشه فقط یک نمونه از این شیء را داشته باشیم. در حال حاضر ممکن است با استفاده چند باره از کلیدواژه new، چندین نمونه از کلاس accounting را داشته باشیم. اگر بخواهیم از الگوی singleton (تلفظ: سینگِلتوُن) استفاده کنیم باید ابتدا constructor درون کلاس Accounting را روی private بگذاریم:
private constructor(id: string, private reports: string[]) { super(id, 'Accounting'); this.lastReport = reports[0]; }
اگر این کار را انجام بدهیم کد زیر به خطا برمی خورد:
const accounting = new AccountingDepartment('d2', []);
به دلیل اینکه constructor روی private تنظیم شده است بنابراین فقط از درون کلاس صدا زده می شود. حالا چطور به کلاس دسترسی داشته باشیم در حالی که اصلا نمی توانیم یک نمونه از شیء خود را بسازیم؟ اینجاست که متدهای استاتیک وارد کار می شوند. اگر یادتان باشد این متدها نیازی به نمونه سازی نداشتند. ابتدا یک خصوصیت جدید به کلاس Accounting اضافه کنید:
class AccountingDepartment extends Department { private lastReport: string; private static instance: AccountingDepartment;
خصوصیت instance از تایپ AccountingDepartment خواهد بود یعنی به عنوان مقدار فقط نمونه هایی از کلاس AccountingDepartment را می گیرد. حالا می توانیم متد استاتیک خودمان را تعریف کنیم:
static getInstance() { if (AccountingDepartment.instance) { return this.instance; } this.instance = new AccountingDepartment('d2', []); return this.instance; }
نام این تابع به سلیقه شما بستگی دارد. من در یک شرط if چک کرده ام که آیا خصوصیت instance وجود دارد یا نه. اگر وجود داشت یعنی از قبل یک نمونه از این کلاس ساخته شده است بنابراین همان instance را برمی گردانیم. بدین صورت فرد به خود کلاس دسترسی پیدا می کند. در غیر این صورت اگر instance ای وجود نداشت با کلیدواژه new یک نمونه دیگر از آن را می سازیم.
حالا برای ساخت یک نمونه از این کلاس، دیگر نمی توانیم از کلیدواژه new استفاده کنیم اما می توانیم از getInstance کمک بگیریم:
// const accounting = new AccountingDepartment('d2', []); const accounting = AccountingDepartment.getInstance();
از آنجایی که دستور قبلی را کامنت کرده ایم، این اولین دستور دریافت نمونه از این کلاس است بنابراین یک نمونه جدید برایمان ارسال می شود اما اگر این کار را دوباره انجام دهم یک نمونه جدید تولید نمی شود:
// const accounting = new AccountingDepartment('d2', []); const accounting = AccountingDepartment.getInstance(); const accounting2 = AccountingDepartment.getInstance();
در کد بالا accounting و accounting2 هر دو به یک نمونه از کلاس AccountingDepartment اشاره می کنند و دو نمونه جدا نیستند. برای اثبات این مسئله می توانیم هر دو را Console.log کنیم:
// const accounting = new AccountingDepartment('d2', []); const accounting = AccountingDepartment.getInstance(); const accounting2 = AccountingDepartment.getInstance(); console.log(accounting, accounting2);
با اجرای دستور بالا خروجی در کنسول مرورگر به شکل زیر خواهد بود:
همانطور که مشاهده می کنید هر دو یک id و اطلاعات یکسانی دارند بنابراین یکی هستند. الگوی singleton روش جالبی است که ممکن است در برخی از موارد بسیار کاربردی باشد اما معمولا و در حالت عادی از آن استفاده نمی کنیم چرا که نیازی به آن نداریم. شاید از من بپرسید که استفاده واقعی الگوی singleton کجاست؟ پاسخ این سوال ساده است، هر جایی که نیاز داشته باشید از هر کلاس فقط یک نمونه تولید شود، نه بیشتر. در زبان هایی مثل زبان جاوا اسکریپت و در حالت عادی آنچنان استفاده ای از متد singleton نداریم مگر اینکه به سلیقه توسعه دهنده باشد. در زبان های برنامه نویسی دیگر مانند PHP از الگوی singleton برای مدیریت کلاس های مربوط به پایگاه های داده استفاده می شود چرا که در هر اسکریپت PHP باید تنها یک اتصال به پایگاه داده داشته باشیم تا برنامه خود را کُند نکنیم.
این قسمت قسمت پایانی کار با کلاس ها بود و در قسمت آینده به سراغ interface ها می رویم بنابراین بهتر است نگاهی خلاصه به مواردی داشته باشیم که در طول این چند قسمت یاد گرفته ایم. ما در رابطه با کلاس ها و نحوه ساخت آن ها صحبت کردیم، همچنین توضیح دادیم که خصوصیت یا property و متد یا method چه چیزهایی هستند. در ادامه در رابطه با Access modifier ها صحبت کردیم که شامل public (عمومی) برای دسترسی آزاد، private (خصوصی) برای دسترسی از درون کلاس، protected (محافظت شده) برای دسترسی از درون کلاس و کلاس های فرزند می شود. ما در مورد overwrite کردن متدهای کلاس پدر و انواع انتزاعی (abstract) و استاتیک (static) مثال هایی ارائه دادیم و توضیح دادیم که برای استفاده از متدهای انتزاعی باید درون کلاس های انتزاعی (abstract class) باشیم. کلاس های انتزاعی در واقع قابل instantiate شدن نیستند و یک نقشه کلی برای کلاس های فرزند خود به حساب می آیند. به همین دلیل است که متدهای انتزاعی، کلاس های فرزند را مجبور می کنند تا بدنه شان را برای همان کلاس بنویسند. در نهایت با constructor های private و الگوی Singletong آشنا شدیم که موقعیت های خاص خودش استفاده های کاربردی دارد.
امیدوارم با خوبی با کلاس ها آشنا شده باشید. اگر از نکاتی که در پاراگراف بالا توضیح دادم چیزی را فراموش کرده اید، پیشنهاد می کنم برگردید و آن ها را دوباره مطالعه کنید. در قسمت بعد به سراغ interface ها و مباحث مربوط به آن ها می رویم.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.