دو مفهوم interface و کلاس جزء مفاهیم اساسی برنامه نویسی شیگرا (OOP) هستند. تایپ اسکریپت یک زبان جاوااسکریپتی شیگرا است که از ES6 به بعد از ویژگیهای OOP مانند interface، کلاس و کپسولهسازی پشتیبانی میکند.
سوالی که ممکن است مطرح شود این است که چه زمانی باید از interfaceها، کلاسها و یا هر دو به طور همزمان استفاده کنیم؟ در این مقاله قصد داریم تا با انواع interface و کلاس آشنا شویم و یاد بگیریم که چه زمانی باید از یک یا هر دوی آنها در تایپ اسکریپت استفاده کنیم.
قبل از شروع، باید بدانیم کلاس تایپ اسکریپت چیست. در برنامه نویسی شیگرا، کلاس یک طرح یا قالب است که به وسیله آن میتوانیم آبجکتهایی با ویژگیها و متدهای خاص ایجاد کنیم.
تایپ اسکریپت سینتکس اضافی برای بررسی تایپ را ارائه میکند و کد را به جاوااسکریپتی که روی هر پلتفرم و مرورگری اجرا میشود تبدیل مینماید. کلاسها در تمامی مراحل دخالت دارند. پس از تبدیل کد تایپ اسکریپت به یک فایل جاوااسکریپت، میتوانیم آنها را در فایلهای نهایی پیدا کنیم.
کلاس الگوی آبجکت را تعریف میکند، یا اینکه آبجکت چیست و چه کاری انجام میدهد را تعیین میکند. در ادامه یک کلاس ساده با یک سری ویژگیها و متدها ایجاد میکنیم تا بتوانیم رفتار آن را ببینیم.
ابتدا از طریق کد زیر یک کلاس Developer
ایجاد میکنیم:
class Developer { name?: string; // string or undefined position?: string; // string or undefined }
ما کلاس را با ویژگیهای name
و position
توصیف میکنیم. آنها تایپهایی مانند string
و undefined
دارند.
در مرحله بعد، از طریق کلاس Developer
و با استفاده از کلمه کلیدی new
یک آبجکت ایجاد میکنیم:
const developer = new Developer(); developer.name // it outputs undefined developer.position // it outputs undefined
هنگامی که developer.name
را فراخوانی میکنیم، مقدار undefined
را برمیگرداند زیرا مقادیر اولیه را تعیین نکردهایم. برای ایجاد یک آبجکت با مقادیر اولیه در تایپ اسکریپت میتوانیم از متد constructor استفاده کنیم. متد constructor برای مقداردهی اولیه و ایجاد آبجکتها استفاده میشود.
اکنون کلاس Developer
خود را با استفاده از کد زیر به روز رسانی میکنیم:
class Developer { name: string; // only string position: string; // only string constructor(name: string, position: string) { this.name = name; this.position = position; } }
در کد بالا، متد constructor
را برای مقداردهی اولیه ویژگیها اضافه کردیم.
اکنون میتوانیم با استفاده از کد زیر name
را به عنوان Gapur
و position
را به عنوان Frontend Developer
تنظیم کنیم:
const developer = new Developer("Gapur", "Frontend Developer"); developer.name // it outputs Gapur developer.position // it outputs Frontend Developer
در نهایت، همانطور که قبلاً هم اشاره کردیم، کلاس متدهایی دارد که تعیین میکند آبجت چگونه باید عمل کند. در این حالت، هر developerای برنامههایی را develop میکند. بنابراین، کلاس Developer
دارای متد develop
است.
یک آبجکت developer
میتواند یک اکشن develop را انجام دهد:
class Developer { name: string; position: string; constructor(name: string, position: string) { this.name = name; this.position = position; } develop(): void { console.log('develop an app'); } }
اگر متد develop
را اجرا کنیم، عبارت console.log
زیر را اجرا میکند:
developer.develop() // it outputs develop an app
interface در تایپ اسکریپت ساختاری است که مانند یک قرارداد در برنامه و یا سینتکسی برای کلاس عمل میکند. interface همچنین به عنوان duck printing و یا subtyping نیز شناخته میشود.
interface شامل یک متد only
و تعریفهای فیلد بدون پیادهسازی میباشد. ما نمیتوانیم از آن برای ایجاد چیزی استفاده کنیم. کلاسی که یک interface را پیادهسازی میکند باید تمام فیلدها و متدها را داشته باشد. بنابراین، ما آن را برای بررسی تایپ به کار میگیریم.
هنگامی که تایپ اسکریپت تمام کدها را به جاوااسکریپت تبدیل میکند، interface از فایل جاوااسکریپت محو میشود. بنابراین، ابزار مفیدی در فار توسعه به شمار میآید.
ما باید از یک interface برای موارد زیر استفاده کنیم:
اکنون میخواهیم با کمک کد زیر یک interface تعریف کنیم:
interface InterfaceName { // variables; // methods; }
ما فقط میتوانیم تعریفهایی از متغیرها و متدها را در بدنه interface داشته باشیم. در ادامه میخواهیم یک interface IDeveloper
برای کلاس Developer
قبلی ایجاد کنیم:
interface IDeveloper { name: string position: string develop: () => void } class Developer implements IDeveloper { name: string; position: string; constructor(name: string, position: string) { this.name = name; this.position = position; } develop(): void { console.log('develop an app'); } }
در کد بالا، interface IDeveloper
ما شامل name
و position
متغیرها است. همچنین متد develop
را هم شامل میشود. در نتیجه کلاس Developer
interface IDveloper
را پیاده سازی میکند. بنابراین، باید دو متغیر و یک متد تعریف کند.
اگر کلاس Developer
هیچ متغیری را پیاده سازی نکند، تایپ اسکریپت یک خطا نشان میدهد:
class Developer implements IDeveloper { // error Class 'Developer' incorrectly implements interface 'IDeveloper'. name: string; constructor(name: string, position: string) { this.name = name; this.position = position; } develop(): void { console.log('develop an app'); } }
اکنون سوالی که مطرح میشود این است که در تایپ اسکریپت چه زمانی باید از کلاس و چه زمانی از interface استفاده کنیم؟
قبل از پاسخ به این سوال میخواهیم در مورد ویژگی static
تایپ اسکریپت صحبت کنیم که به ما این امکان را میدهد تا از فیلدها و متدهای کلاسها بدون ایجاد نمونه کلاس استفاده کنیم.
اکنون قصد داریم تا با استفاده از کلاس Developer
قبلی یک کلاس با متد static بسازیم:
class Developer { static develop(app: { name: string, type: string }) { return { name: app.name, type: app.type }; } }
اکنون میتوانیم متد Developer.develop()
را بدون نمونهسازی کلاس فراخوانی کنیم:
Developer.develop({ name: 'whatsapp', type: 'mobile' }) // outputs: { "name": "whatsapp", "type": "mobile" }
همچنین میتوانیم از کلاسها برای بررسی تایپ در تایپ اسکریپت استفاده کنیم. در ادامه با استفاده از کد زیر یک کلاسApp
ایجاد میکنیم:
class App { name: string; type: string; constructor(name: string, type: string) { this.name = name; this.type = type; } }
کلاسDeveloper
خود را تغییر میدهیم:
class Developer { static develop(app: App) { return { name: app.name, type: app.type }; // output the same } }
اکنون یک نمونه از App
را میسازیم و Developer.develop()
را با یک argument object فراخوانی میکنیم:
const app = new App('whatsapp', 'mobile'); Developer.develop(app); // outputs the same: { "name": "whatsapp", "type": "mobile" }
Developer.develop(app)
و Developer.develop({ name: 'whatsapp', type: 'mobile' })
هر دو محتوای یکسانی را تولید میکنند. اما رویکرد دوم خواناتر است و انعطافپذیری بیشتری دارد.
علاوه بر این میتوانیم نوع آرگومانها را هم بررسی کنیم. برای انجام این کار باید یک آبجکت ایجاد کنیم. اینجاست که مفهوم interfaceها مطرح میشود.
ابتدا میخواهیم کلاس App
را با استفاد از کد زیر به یک interface تغییر دهیم:
interface App { name: string type: string } class Developer { static develop(app: App) { return { name: app.name, type: app.type }; // output the same } }
در کد بالا، بدنه کلاس Developer
را تغییر ندادیم و نمونهای از App
ایجاد نکردیم، اما نتیجه یکسان بود. همینطور زمان کوتاهتر بوده و تعداد خط کد نیز کمتر از قبل میباشد.
چه زمانی باید از کلاسها و interfaceها استفاده کنیم؟ به طور خلاصه اگر میخواهیم یک type-checked class object ایجاد و ارسال کنیم، باید از کلاسهای تایپ اسکریپت استفاده کنیم. اما اگر نیاز داریم بدون ایجاد آبجت کار کنیم، استفاده از interfaceها بهترین انتخاب است.
در نهایت، ما دو رویکرد مفید blueprint و contract را بررسی کردیم که میتوانیم بسته به شرایط پروژه از هر دوی آنها باهم و یا به صورت جداگانه استفاده کنیم.