وراثت در تایپ اسکریپت

Inheritance in TypeScript

10 مرداد 1399

تا این قسمت، کلاس department را تعریف کرده ایم که یک نقشه کلی برای دپارتمان های شرکت فرضی ما است اما ممکن است بعضی از دپارتمان ها پیچیده تر از این حدود ها باشند. به طور مثال admin ها فقط در دپارتمان IT حضور دارند و در دپارتمان های دیگر Admin نداریم. این مسئله را نمی توانیم با یک کلاس کلی مثل Department پیاده سازی کنیم. بنابراین برخی از خصوصیات و متدها مانند name و id در همه دپارتمان ها موجود هستند اما خصوصیات یا متدهایی نیز وجود دارند که مخصوص هر دپارتمان خاص هستند. اینجاست که مبحث وراثت یا inheritance به کمک ما می آید.

به طور مثال در همان فایل app.ts خودمان، پایین تر از تعریف کلاس Department یک کلاس دیگر به نام ITDepartment تعریف می کنیم:

class ITDepartment extends Department {
    
}

کلمه extend یعنی «توسعه می دهد» بنابراین زمانی که می خواهیم از وراثت استفاده کنیم باید از این کلیدواژه استفاده کنیم. ما در کد بالا اعلام کرده ایم که کلاس ITDepartment کلاس پدر خود یعنی Department را توسعه می دهد یا به عبارت ساده تر کدهای آن را به ارث می برد. توجه داشته باشید که هر کلاس فقط می تواند از یک کلاس دیگر ارث بری داشته باشد و نمی توانید بگویید ITDepartment از کلاس های x و y و z ارث می برد. حالا می توانیم نمونه سازی از کلاس ITDepartment را دقیقا مانند کلاس پدرش یعنی Department انجام دهیم:

const accounting = new ITDepartment ('d1', 'Accounting');

چرا؟ به دلیل اینکه ITDepartment تمام کدهای Department را اعم از constructor به ارث می برد. البته می توانیم خودمان نیز یک constructor دیگر تعریف کنیم تا از آن استفاده کند:

class ITDepartment extends Department {
  constructor() {
    
  }
}

من در اینجا قصد دارم که id این دپارتمان را با در constructor خودش بگیرم اما برای ساخته شدن، آن را به constructor کلاس پدر ارسال کنم. برای انجام این کار باید از تابع ویژه ای به نام super استفاده کنیم:

class ITDepartment extends Department {
  constructor(id: string) {
    super(id, 'IT');
  }
}

با صدا زدن تابع super، در واقع constructor کلاس پدر را صدا زده ایم! Id را به صورت یک پارامتر گرفته بودیم بنابراین همان را به پارامتر اول super پاس می دهیم و name را که پارامتر دوم constructor کلاسِ پدر بود به صورت دستی (IT) قرار داده ایم چرا که نام این دپارتمان همیشه IT خواهد بود و نیازی به وارد کردن آن به صورت پارامتری نیست. توجه داشته باشید که اگر قرار است از super در constructor خود استفاده کنید باید super اولین چیزی که می نویسید باشد و قبل از هر چیز دیگری آن را صدا کنید.

من می خواهم یک خصوصیت دیگر به نام admins را نیز به این کلاس اضافه کنم بنابراین:

class ITDepartment extends Department {
  constructor(id: string, public admins: string[]) {
    super(id, 'IT');
  }
}

پارامتر دوم این constructor که admins است دارای یک access modifier می باشد و در جلسه قبل توضیح دادم که این کار یک روش خلاصه برای تعریف و مقداردهی به خصوصیات جدید است. اگر از روش عادی آن برویم، باید چنین کدی بنویسیم:

class ITDepartment extends Department {
  admins: string[];
  constructor(id: string, admins: string[]) {
    super(id, 'IT');
    this.admins = admins;
  }
}

همانطور که می بینید اولین کار من صدا زدن super بوده است تا به خطا برخورد نکنیم. در نهایت می توانیم یک نمونه از این کلاس بسازیم:

const it = new ITDepartment('d1', ['Max']);

حالا یک کلاس دیگر برای دپارتمان accounting (حسابداری) می سازیم:

class AccountingDepartment extends Department {

}

این کلاس نیز کلاس department را extend می کند. در اولین قدم یک constructor جدید برای این کلاس تعریف می کنم اما در عین حال از constructor کلاس پدر نیز استفاده می کنم:

class AccountingDepartment extends Department {
  constructor(id: string, private reports: string[]) {
    super(id, 'Accounting');
  }
}

از کد بالا تشخیص داده می شود که دو خصوصیت داریم: id و reports (به معنی گزارشات). در مرحله بعد یک متد به نام addReport می نویسم که مخصوص ثبت کردن گزارشات این دپارتمان است:

class AccountingDepartment extends Department {
  constructor(id: string, private reports: string[]) {
    super(id, 'Accounting');
  }

  addReport(text: string) {
    this.reports.push(text);
  }
}

همانطور که می بینید این متد بسیار ساده است. آخرین متدی که به این کلاس اضافه می کنم نیز یک متد به نام printReports است که مسئولیت چاپ کردن گزارشات در کنسول مرورگر را بر عهده دارد:

class AccountingDepartment extends Department {
  constructor(id: string, private reports: string[]) {
    super(id, 'Accounting');
  }

  addReport(text: string) {
    this.reports.push(text);
  }

  printReports() {
    console.log(this.reports);
  }
}

در نهایت یک نمونه از این کلاس را می سازیم تا مطمئن شویم همه چیز به خوبی کار می کند:

const accounting = new AccountingDepartment('d2', []);

accounting.addReport('Something went wrong...');

accounting.printReports();

من گزارشات را به صورت یک آرایه خالی پاس داده ام چرا که در اول کار هنوز هیچ گزارشی نداریم. سپس یک گزارش جدید اضافه کرده ام و گفته ام که «مشکلی پیش آمده است» (این فقط یک مثال است و شما می توانید هر گزارش دیگری را در این قسمت قرار دهید) و نهایتا این گزارشات را در کنسول مرورگر چاپ کرده ام:

[“Something went wrong…”]

بدین صورت می توانیم با استفاده از مفهوم وراثت، یک کلاس پایه و مشترک داشته باشیم که طرحی کلی از برنامه ما ارائه دهد و سپس کلاس های کوچک تری داشته باشیم که هر کدام مسئول بخشی از برنامه ما باشند. این کلاس های کوچک تر با استفاده از مفهوم وراثت می توانند علاوه بر خصوصیات و متدهای کلاس پدر، خصوصیات و متدهای خودشان را نیز دریافت کنند.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری آموزش جامع تایپ اسکریپت توصیه می‌کند:
نویسنده شوید

دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

مقالات مرتبط
ما را دنبال کنید
اینستاگرام روکسو تلگرام روکسو ایمیل و خبرنامه روکسو