رابط (interface)
رابطها، شبیه به کلاسها هستند، اما فقط شامل تعاریفی برای متدها و خواص (Property) میباشند. رابطها را میتوان به عنوان پلاگین های کلاسها در نظر گرفت. کلاسی که یک رابط خاص را پیاده سازی میکند، لازم است که کدهایی برای اجرا توسط اعضاء و متدهای آن فراهم کند چون اعضا و متدهای رابط هیچ کد اجرایی در بدنه خود ندارند. اجازه دهید که نحوه تعریف و استفاده از یک رابط در کلاس را توضیح دهیم :
1: <?php 2: interface ISample 3: { 4: function ShowMessage($message); 5: } 6: 7: class Sample implements ISample 8: { 9: public function ShowMessage($message) 10: { 11: echo $message; 12: } 13: } 14: 15: $sample = new Sample(); 16: $sample->ShowMessage("Implemented the ISample Interface!"); 17: ?>
Implemented the ISample Interface!
در خطوط 5-2 یک رابط به نام ISample تعریف کردهایم. بر طبق قراردادهای نامگذاری، رابطها به شیوه پاسکال نامگذاری میشوند و همه آنها باید با حرف I شروع شوند. یک متد در داخل بدنه رابط تعریف میکنیم (خط 4). به این نکته توجه کنید که متد تعریف شده فاقد بدنه است و در آخر آن باید از سیمیکولن استفاده شود.
وقتی که متد را در داخل رابط تعریف میکنید فقط لازم است که عنوان متد (نوع، نام و پارامترهای آن) را بنویسید. به این نکته نیز توجه کنید که متدها و خواص تعریف شده در داخل رابط سطح دسترسی ندارند، چون باید همیشه هنگام اجرای کلاسها در دسترس باشند. برای پیاده سازی یک interface توسط یک کلاس از کلمه کلیدی implements استفاده میشود. کلاسی که اینترفیس را اجرا میکند، کدهای واقعی را برای اعضای آن فراهم میکند. همانطور که در مثال بالا میبینید کلاس Sample، متد ShowMessage() رابط ISample را اجرا و تغذیه میکند. برای روشن شدن کاربرد رابطها به مثال زیر توجه کنید:
1: <?php 2: class CA 3: { 4: public $FullName; 5: public $Age; 6: 7: public function __construct($fullname, $age) 8: { 9: $this->FullName = $fullname; 10: $this->Age = $age; 11: } 12: } 13: 14: class CB 15: { 16: public $FirstName; 17: public $LastName; 18: public $PersonsAge; 19: 20: public function __construct($firstname, $lastname, $personage) 21: { 22: $this->FirstName = $firstname; 23: $this->LastName = $lastname; 24: $this->PersonsAge = $personage; 25: } 26: } 27: 28: function PrintInfo(CA $item) 29: { 30: printf("Name: %s, Age: %d", $item->FullName, $item->Age); 31: } 32: 33: $a = new CA("John Doe", 32); 34: 35: PrintInfo($a); 36: ?>
Name: John Doe, Age: 32
در کد بالا دو کلاس CA و CB تعریف شدهاند، در کلاس CA دو فیلد به نام FullName و Age و در کلاس CB سه فیلد به نامهای LastName ،PersonsAge و FirstName تعریف کردهایم. در خطوط 43-40 یک متد به نام PrintInfo داریم که یک پارامتر از نوع کلاس CA قبول می کند. به شکل ساده در این متد مقدار فیلدهای شی ای که به این متد ارسال شده است، چاپ میشود. در خط 33 یک شیء از کلاس CA ساختهایم و فیلدهای آن را مقدار دهی کردهایم. سپس این شیء را به متد PrintInfo ارسال میکنیم. کلاسهای CA و CB از نظر مفهومی شبیه یکدیگر هستند. مثلاً کلاس CA فیلد FullName را برای نمایش نام و نام خانوادگی دارد، ولی کلاس CB برای نمایش نام و نام خانوادگی، دو فیلد جدا از هم به نامهای FirstName و LastName را دارد. و همچنین یک فیلد برای نگهداری مقدار سن داریم که در کلاس CA نام آن Age و در کلاس CB نام آن PersonAge میباشد. مشکل اینجاست که اگر ما یک شیء از کلاس CA را به متد (PrintInfo) ارسال کنیم از آنجایی که در داخل بدنه این متد فقط مقدار دو فیلد چاپ میشود اگر بخواهیم یک شیء از کلاس CB را به آن ارسال کنیم که دارای سه فیلد است با خطا مواجه میشویم (زیرا متد PrintInfo با ساختار کلاس CA سازگار است و فیلدهای CB را نمیشناسد). برای رفع این مشکل باید ساختار دو کلاس CA و CB را شبیه هم کنیم و این کار را با استفاده از Interface انجام میدهیم.
1: <?php 2: interface IInfo 3: { 4: function GetName(); 5: function GetAge(); 6: } 7: 8: class CA implements IInfo 9: { 10: public $FullName; 11: public $Age; 12: 13: public function __construct($fullname, $age) 14: { 15: $this->FullName = $fullname; 16: $this->Age = $age; 17: } 18: 19: public function GetName() { return $this->FullName; } 20: public function GetAge() { return $this->Age; } 21: } 22: 23: class CB implements IInfo 24: { 25: public $FirstName; 26: public $LastName; 27: public $PersonsAge; 28: 29: public function __construct($firstname, $lastname, $personage) 30: { 31: $this->FirstName = $firstname; 32: $this->LastName = $lastname ; 33: $this->PersonsAge = $personage; 34: } 35: 36: public function GetName() { return $this->FirstName . ' ' . $this->LastName; } 37: public function GetAge() { return $this->PersonsAge; } 38: } 39: 40: function PrintInfo(IInfo $item) 41: { 42: printf("Name: %s, Age: %d", $item->GetName(), $item->GetAge()); 43: } 44: 45: $a = new CA("John Doe", 32); 46: $b = new CB("Jane", "Doe", 33); 47: 48: PrintInfo($a); 49: echo '<br/>'; 50: PrintInfo($b); 51: ?>
Name: John Doe, Age: 32 Name: Jane Doe, Age: 33
کد بالا را میتوان به اینصورت توضیح داد که در خط 6-2 یک رابط به نام IInfo تعریف و آن را در خطوط 8 و 23 توسط دو کلاس CA و CB پیاده سازی کردهایم. چون این دو کلاس وظیفه دارند متدهای این رابط را پیاده سازی کنند پس در خطوط 20-19 و 37-36 کدهای بدنه دو متد این رابط را آن طور که میخواهیم، مینویسیم. در خط 40 متد ()PrintInfo را طوری دستکاری میکنیم که یک پارامتر از نوع رابط دریافت کند. حال زمانی که دو شیء از دو کلاس CA و CB در دو خط 45 و 46 ایجاد میکنیم و آنها را در دو خط 48 و 50 به متد ()PrintInfo ارسال میکنیم، چونکه این دو کلاس رابط IInfo را پیاده سازی کردهاند به طور صریح به رابط تبدیل میشود. یعنی کلاسی که یک رابط را پیاده سازی کند به طور صریح میتواند به رابط تبدیل شود. حال بسته به اینکه شیء کدام کلاس به متد ()PrintInfo ارسال شده است، متد مربوط به آن کلاس فراخوانی شده و مقادیر فیلدها چاپ میشود. میتوان چند رابط را به وسیله یک کلاس پیاده سازی کرد.
class Sample implements ISample1, ISample2, ISample3 { //Implement all interfaces }
درست است که میتوان از چند رابط در کلاس استفاده کرد ولی باید مطمئن شد که کلاس میتواند همه اعضای رابطها را تغذیه کند. اگر یک کلاس از کلاس پایه ارث ببرد و در عین حال از رابطها هم استفاده کند، در این صورت باید نام کلاس پایه قبل از نام رابطها ذکر شود. به شکل زیر :
class Sample extends BaseClass implements ISample1, ISample2 { }
همچنین میتوان از عملگر instanceof برای چک کردن اینکه آیا یک شیء خاص از یک رابط استفاده میکند یا نه استفاده کرد :
$sample = new Sample(); if(sample instanceof ISample) { echo "sample implements the ISample Interface!"; }
نکته دیگر اینکه نمیتوان از یک رابط نمونهای ایجاد کرد چون رابطها دارای سازنده نیستند، مثلاً کد زیر اشتباه است :
$isample = new ISample();
رابطها حتی میتوانند از رابطهای دیگر ارث بری کنند. به مثال زیر توجه کنید :
1: <?php 2: interface IBase 3: { 4: function BaseMethod(); 5: } 6: 7: interface ISample extends IBase 8: { 9: function ShowMessage($message); 10: } 11: 12: class Sample implements ISample 13: { 14: public function ShowMessage($message) 15: { 16: echo($message); 17: } 18: 19: public function BaseMethod() 20: { 21: echo "Method from base interface!"; 22: } 23: } 24: 25: 26: $sample = new Sample(); 27: 28: $sample -> ShowMessage("Implemented the ISample Interface!"); 29: echo '<br/>'; 30: $sample -> BaseMethod(); 31: ?>
Implemented the ISample Interface! Method from base interface!
مشاهده میکنید که حتی اگر کلاس Sample فقط رابط ISample را پیاده سازی کند، لازم است که همه اعضای IBase را هم پیاده سازی کند چون ISample از آن ارث بری میکند (خط 7).