کلاس ها و متدهای انتزاعی: مفاهیم اولیه

درسنامه درس 7 از سری شی گرایی در PHP
php-oop-abstract-method-and-class

با سلام و عرض ادب، تا به اینجای کار مبحث وراثت را به اتمام رساندیم و حالا نوبت به کلاس ها و متدهای Abstract (انتزاعی) می رسد. برای درک این مطلب ابتدا دو مثال خدمت شما ارائه میکنم. اول از همه مثال ماشین همیشگی خود را فراموش کرده و از مثال جدیدی استفاده میکنیم تا ذهن شما تنها روی یک مورد قفل نشود.

شروع یک مثال برای کلاس های انتزاعی (Abstract Class) در شی گرایی

تصور کنید کلاس پدری داریم به نام محصولات (product). حتما به یاد دارید که کلاس پدر حاوی ویژگی های مشترک فرزندان بود. فرزندان کلاس product می توانند کلاس Books (کتاب ها) و Multimedia (چند رسانه ای) باشند. بیایید به مثال خود یک زمینه هم بدهیم. فرض کنید که قرار است در فروشگاه خود مجموعه ای از محصولات را به فروش برسانیم و این محصولات شامل کتاب ها و محصولات چند رسانه ای (مانند فیلم های آموزشی، فایل های صوتی، لوح فشرده و ...) هستند.

اگر بخواهیم چنین حالتی را با مثال تصویری نمایش بدهیم می توان به این شکل از آن استفاده کرد:

تصویر شماره 1: مثال فروشگاه در مفاهیم شی گرایی در PHP
تصویر شماره 1: مثال فروشگاه

در جلسات قبلی چنین مثالی برای ما ساده بود؛ کلاس پدر ویژگی های مشترکی دارد و آن ها را برای بچه ها به ارث می گذارد! حالا تصور کنید که مثل همیشه کلاس پدر را می سازیم و کلاس های فرزند را هم ایجاد میکنیم و خوش و خرم می رویم دنبال کارمان! اما به نکته ای توجه نکردیم!

  • آیا واقعا نیازی به کلاس پدر وجود دارد؟
  • آیا شما هم متوجه شده اید که کلاس پدر کار خاصی نمیکند و فقط یک سری از خصوصیات مشترک را به ارث میگذارد؟
  • حتی اگر فروشگاه ما محصولات دیگری هم بیاورد و ما انواع کلاس های فرزند دیگر را بسازیم آیا خودِ کلاسِ پدر را مجددا خواهیم ساخت؟

همانطور که شما حدس میزدید، جواب منفی است

کلاس Product یک کلاس اضافی است و فقط جا می گیرد. اما اگر این کلاس را حذف کنیم دیگر جایی برای نگهداری خصوصیات مشترک نخواهیم داشت و باید مبحث وراثت را فراموش کنیم. این مشکل چطور حل می شود؟

کلید حل مشکل ما کلاس های انتزاعی هستند. اگر یک کلاس را به طور انتزاعی تعریف کنیم تمام نکات مثبت وراثت را خواهیم داشت و از طرفی هم از ایجاد شیء کلاس پدر جلوگیری میکنیم. به عبارت دیگر کلاسی به نام کلاس پدر خواهیم داشت اما آن کلاس به شیء تبدیل نخواهد شد چرا که به خودِ آن کلاس نیازی نداریم.

موارد مصرف کلاس های انتزاعی به شما و پروژه ی شما بستگی دارد اما از مثال های کاربردی آن همین مثال فروشگاه ما بود.

مثالی دیگر از کلاس های انتزاعی در شی گرایی

مثال دیگری که می توان در این زمینه ارائه داد اشکال هندسی است. ما می دانیم که مستطیل، مثلث، مربع، دایره و ... همه از اشکال هندسی دو بعدی هستند و از آنجایی که دو بعدی هستند همگی دارای خاصیتی به نام محیط می باشند. بر اساس چیز هایی که از قسمت های قبلی یاد گرفته ایم می توان این ویژگی مشترک (محیط) را در کلاس پدر قرار داد و بر اساس چیزی که این جلسه گفتیم، نمونه سازی از این کلاسِ پدر کاری بیهوده است.

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

بر اساس تعاریف ارائه شده یک کلاس انتزاعی در اکثر اوقات متدهای انتزاعی (Abstract Method) نیز دارد. متدهای انتزاعی فقط می توانند نام و آرگومان داشته باشند و دیگر هیچ کدی داخلشان نیست. چرا؟

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

بررسی مفهوم کلاس ها و متدهای انتزاعی برای مثال ماشین در شی گرایی PHP

خیلی خب، برگردیم به مثال همیشگی ماشین خودمان که حسابی دلمان برایش تنگ شده است! با توجه به جلسات قبل اگر بخواهیم کلاسی به نام Car را به صورت انتزاعی بنویسیم از کلید واژه ی abstract استفاده می کنیم. مثال:

abstract class Car { }

برای ایجاد متدهای انتزاعی هم دقیقا مانند کلاس ها عمل میکنیم و قبل از نام آنها از کلید واژه ی abstract استفاده می کنیم. این متدهای انتزاعی درون یک کلاس انتزاعی قرار می گیرند و هیچ بدنه ای ندارند، بلکه همانطور که گفته شد تنها دو چیز دارند:

  1. نام
  2. پارامتر (آرگومان های داخل پرانتز که می توانند خالی نیز باشند)

در مثال زیر، ما متدی انتزاعی به نام ()calcNumMilesOnFullTank میسازیم و آن را داخل کلاس Car قرار می دهیم:

// نمونه ای از یک کلاس و متدِ انتزاعی
abstract class Car {
  abstract public function calcNumMilesOnFullTank();
}

نکته:

عبارت calcNumMilesOnFullTank کوتاه شده ی عبارت "calculate the number of miles a car can drive on a full tank" است که به فارسی می شود "تعداد مایل هایی را که یک ماشین با یک باک پر می تواند برود، محاسبه کن".

چرا این متد انتزاعی است؟

زیرا فرمول محاسبه ی مصرف سوخت هر ماشین در یک مسافت معین متفاوت است و به انواع فاکتور ها مانند حجم باک، نوع موتور ماشین، وزن ماشین و ... بستگی دارد.

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

به مثال زیر توجه کنید:

abstract class Ch2_Product
{

// خصوصیات در این قسمت تعریف شده اند
protected $_type;
protected $_title;

// متدها در این قسمت تعریف شده اند
public function getProductType()
{
return $this->_type;
}
public function getTitle()
{
return $this->_title;
}
abstract public function display();

}

در اینجا کلاسی انتزاعی با نام Ch2_Product ایجاد شده است و خصوصیات و متدهایی نیز برایش تعریف شده است. در آخر این کلاس یک متد انتزاعی به نام display (به معنی نمایش دادن) وجود دارد که خالی است. این متد اعلام میکند که تمامی کلاس هایی که با کلاس پدر رابطه ی فرزندی دارند باید متد display را دارا باشند و اگر غیر این باشد با خطا مواجه خواهیم شد.

اینجا متوجه می شوید که با این روش دیگر نیازی به override کردن کد های کلاس پدر نیست و تنها لازم است متد را به شکل انتزاعی تعریف کنیم تا هر کلاس کد های خودش را داشته باشد.

مثلا یکی از کلاس های فرزند می تواند چنین کدی داشته باشد:

public function display()
{
echo "<p>Book: $this->_title ($this->_pageCount pages)</p>";
}

و نتیجه ی این کد بستگی به مقادیر خصوصیات title و ... دارد. در این رابطه در جلسات قبل مفصلا صحبت کرده ایم.

امیدوارم از این قسمت از آموزش برنامه نویسی شیء گرا استفاده ی مفید کرده باشید.

تا قسمت بعدی، یا حق.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری شی گرایی در PHP توصیه می‌کند:
نویسنده شوید

دیدگاه‌های شما (1 دیدگاه)

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

علی
12 شهریور 1398
این قسمت برام نامفهموم بود مخصوصا که تو آخرین مثالتون گفتین کلاس میخوایم تعریف کنیم اما تو کد تعریف متد رو زده بودین

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

امیر زوارمی
14 شهریور 1398
سلام دوست عزیز، توی آخرین مثال گفتیم این متد رو در یک کلاس فرضی در نظر بگیرید؛ یعنی کلاسی ننوشتیم، فقط گفتیم فرض کنید کلاس های فرزندی وجود داشته باشن و یکیشون چنین تابعی رو داخلش داشته باشه. مقدار خود تابع هم ساده است: نمایش عنوان کتاب (title) و تعداد صفحات (pageCount). بیشتر مفهوم کلی در نظر هست توی این مثال و قصد کد نویسی نداشتیم. میخوایم بگیم چطور میشه از این دسته از کلاس ها به شکل کاربردی استفاده کرد.

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