با سلام و عرض ادب، در قسمت قبلی با مفاهیم اولیه و پایه ای کلاس ها و متدهای انتزاعی آشنا شدیم. در این قسمت قصد داریم نگاهی عمیق تر به این مبحث داشته باشیم و به برخی از سوالات اصلی که فکر برنامه نویسان تازه کار را اشغال می کند جواب بدهیم.
در قسمت قبلی مثالی از یک فروشگاه ارائه شد که کد آن به شکل زیر بود:
abstract class Ch2_Product { // خصوصیات در این قسمت تعریف شده اند protected $_type; protected $_title; // متدها در این قسمت تعریف شده اند public function getProductType() { return $this->_type; } public function getTitle() { return $this->_title; } abstract public function display(); }
اگر به جلسه ی قبل سری نزده اید، حتما قبل از ادامه ی این جلسه به عقب برگردید و مطالعه ی سریعی داشته باشید. در این جلسه به مثال همیشگی ماشین خود بر میگردیم.
بله، کلاس های انتزاعی می توانند متدهای غیر انتزاعی داشته باشند. این کلاس ها حتی می توانند property (خصوصیت) داشته باشند و می دانیم که
خصوصیت ها نمی توانند abstract (انتزاعی) باشند
به مثال زیر توجه کنید:
abstract class Car { // کلاس های انتزاعی می توانند خصوصیات مختلفی داشته باشند protected $tankVolume; // کلاس های انتزاعی میتوانند متدهای غیر انتزاعی داشته باشند public function setTankVolume($volume) { $this -> tankVolume = $volume; } // متد انتزاعی abstract public function calcNumMilesOnFullTank(); }
در مثال بالا ابتدا یک خصوصیت به نام tankVolume$ ایجاد کرده ایم و آن را از نوع protected قرار داده ایم. این عبارت در فارسی به معنای "حجم باک" است. اگر مواردی مانند protected، private و public را از یاد برده اید به مقاله زیر مراجعه کنید:
سپس متد setTankVolume را (که نوعی setter است) تعریف کرده و در آخر نیز یک متد انتزاعی به آن افزوده ایم. این کد به راحتی کار میکند و هیچ نقصی ندارد؛ به این ترتیب ثابت می شود که کلاس های انتزاعی میتوانند متدهای غیرانتزاعی داشته باشند.
شاید تا اینجای کار برای شما سوال پیش آمده باشد که آیا ساخت فرزند برای کلاس های انتزاعی با ساخت فرزند برای کلاس های عادی تفاوتی دارد؟ بله تفاوتی وجود دارد اما تفاوت بزرگی نیست.
ساخت کلاس فرزند برای کلاس های انتزاعی مانند کلاس های عادی با کلید واژه ی extends صورت می گیرد و تنها تفاوت آن در این است که کلاس های فرزند موظف هستند تا برای متدهای انتزاعی کد بنویسند. اگر یادتان باشد در جلسه ی قبل گفتیم که متدهای انتزاعی هیچ بدنه ای (کدی) ندارند و تنها نام و پارامتر میگیرند. وظیفه ی نوشتن کد های داخل این متدها بر عهده ی تک تک کلاس های فرزند است.
اگر بخواهیم در مثال بالا (calcNumMilesOnFullTank) یک کلاس فرزند ایجاد کنیم، کد آن به شکل زیر خواهد بود:
class Honda extends Car { // تعریف بدنه برای متد انتزاعی public function calcNumMilesOnFullTank() { $miles = $this -> tankVolume*30; return $miles; } }
از آن جایی که کلاس Honda فرزند کلاس Car محسوب می شود بنابراین کد های آن را هم به ارث می برد. مهم ترین قطعه کدی که به ارث می برد نیز همان متد انتزاعیِ calcNumMilesOnFullTank است و بر اساس مطالبی که مرور کردیم این کلاس باید برای متد calcNumMilesOnFullTank بدنه بنویسد.
ما در این نمونه کد فرض را بر این گذاشته ایم که فرمول میزان مصرف سوخت برای یک هوندا معادل است با حجم باک ضربدر 30. این تنها یک فرض غیر واقعی برای حل مسئله است و نیازی نیست خودتان را درگیر ماشین ها و فرمول های ریاضی کنید!
سپس یک کلاس فرزند دیگر تعریف میکنیم و نام آن را Toyota می گذاریم. فرقی نمی کند دفعه ی چندم باشد، همین که این کلاس فرزند کلاس Car است، باید برای calcNumMilesOnFullTank بدنه بنویسد:
class Toyota extends Car { // نوشتن بدنه برای متد انتزاعی public function calcNumMilesOnFullTank() { return $miles = $this -> tankVolume*33; } public function getColor() { return "beige"; } }
اگر دقت کنید علاوه بر نوشتن بدنه برای متد انتزاعی، یک متد اختصاصی نیز برای خودش نوشته ایم. این متد که getColor نام دارد رنگ ماشین را به ما برمیگرداند که در این مثال "بژ" است.
برای تمرین آخر این دو مثال را ادغام می کنیم و شیء ای به نام Toyota1 میسازیم و حجم باک آن را 10 لیتر در نظر میگیریم.
$toyota1 = new Toyota(); $toyota1 -> setTankVolume(10); echo $toyota1 -> calcNumMilesOnFullTank();// 330را بر میگرداند echo $toyota1 -> getColor();//خواهد بود beige کلمه ی برگشتی
خروجی کد calcNumMilesOnFullTank همانطور که در خود کد به صورت کامنت آورده شده است 330 است و رنگ ماشین هم که از قبل بژ تعیین شده بود.
نکته: لزومی ندارد که کلاس های انتزاعی دارای متدهای انتزاعی باشند اما اگر در کلاسی متد انتزاعی وجود داشته باشد باید حتما کلاس هم انتزاعی تعیین شود.
کاری که برای کلاس Car و فرزندانش انجام دادیم می توانیم برای مثال فروشگاهمان نیز انجام دهیم. کد اولیه ی فروشگاه ما به این شکل بود:
abstract class Ch2_Product { // خصوصیات در این قسمت تعریف شده اند protected $_type; protected $_title; // متدها در این قسمت تعریف شده اند public function getProductType() { return $this->_type; } public function getTitle() { return $this->_title; } abstract public function display(); }
حالا باید برای تابع display بدنه بنویسیم. ابتدا این کار را برای کلاس کتاب (Book) انجام می دهیم:
public function display() { echo "<p>Book: $this->_title ($this->_pageCount pages)</p>"; }
سپس برای کلاس DVD یا Multimedia و یا هر اسمی که شما برایش انتخاب کرده باشید:
public function display() { echo "<p>DVD: $this->_title ($this->_duration)</p>"; }
همچنین می توانید عین کد های تست و نتیجه گیری که برای کلاس Car نوشته ایم، برای کلاس محصولات یا Product بنویسید. اگر نمیدانید کلاس Product چیست به قسمت قبلی سری بزنید. در این قسمت، مثالی از یک فروشگاه زدیم و اسم کلاس پدر را Product گذاشتیم.
برای جمع بندی نهایی یک مثال کامل از این مسئله را خدمت شما ارائه می کنیم:
<?php abstract class AbstractClass { abstract protected function prefixName($name); } class ConcreteClass extends AbstractClass { public function prefixName($name, $separator = ".") { if ($name == "Pacman") { $prefix = "Mr"; } elseif ($name == "Pacwoman") { $prefix = "Mrs"; } else { $prefix = ""; } return "{$prefix}{$separator} {$name}"; } } $class = new ConcreteClass; echo $class->prefixName("Pacman"), "\n"; echo $class->prefixName("Pacwoman"), "\n"; ?>
خروجی کد بالا عبارت های زیر خواهند بود:
Mr. Pacman Mrs. Pacwoman
نکاتی در مورد کد بالا:
1- بدنه (در انگلیسی Body) در در کد ها یعنی یک بلوک از کد ها. به طور مثال یک تابع به شکل زیر را در نظر بگیرید:
function writeMsg() { echo "Hello world!"; }
قسمتی که مساوی با (;"!echo "Hello world) است بدنه ی این تابع است؛ یعنی یک گروه کد از یک خط تا 1000 خط. وقتی می گوییم بدنه نویسی منظومان این است که کد های داخل متد (که یک تابع است) را برایش بنویسیم. چرا؟ چون متد انتزاعی است و هیچ بدنه یا کدی ندارد. به قست قبلی مراجعه کنید، در این رابطه صحبت کرده ایم.
2- پارامتر (parameter) یا آرگومان (argument) همان مقادیری هستند که در پرانتز های جلوی نام تابع قرار می گیرند اما یک تفاوت دارند:
function familyName($fname) { echo "$fname Refsnes.<br>"; }
در اینجا fname$ یک پارامتر است.
familyName("Kai Jim");
در اینجا "Kai Jim" یک آرگومان است.
امیدوارم از این قسمت استفاده ی کافی را برده باشید. در قسمت بعد سراغ مباحث مختلفی از جمله interface ها می رویم که به کلاس های انتزاعی بسیار شبیه هستند.
تا قسمت بعد یا حق.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.