رابط (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 سه فیلد به نام های FirstName و LastName , PersonsAge تعریف کرده ایم. در کلاس Program یک متد به نام PrintInfo داریم که یک پارامتر از نوع کلاس CA دارد. به شکل ساده در این متد مقدار فیلد های شی ای که به این متد ارسال شده است چاپ می شود. در متد Main یک شی از کلاس 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).

لطفا اگر نظر، پیشنهاد و یا انتقادی در باره مطلب بالا دارید در قسمت زیر مطرح بفرمایید.