پیش از این گفتیم که در برخی زبانهای برنامهنویسی شیئگرا همچون PHP امکان به اصطلاح Multiple Inheritance نداریم. به عبارتی، این امکان در اختیار توسعهدهندگان این زبان قرار نگرفته تا بتوانند در آنِ واحد از بیش از یک کلاس در ارثبری کنند. در عین حال، فیچری در این زبان داریم تحت عنوان Trait که این امکان را در اختیارمان میگذارد تا به نوعی این مشکل را مرتفع سازیم. به طور کلی، برخی از خصیصههای Trait عبارتند از:
- از نوشتن کدهای تکراری جلوگیری میکند.
- از روی آن نمیتوان آبجکت ساخت.
- میتواند متدهایی با سطوح دسترسی protected
،public
و private
داشته باشد.
به منظور درک بهتر سازوکار این فیچر در زبان پیاچپی، داخل پوشهٔ oop
پروژهای تحت عنوان trait
ساخته و با مبنا قرار دادن ساختار پروژهای که در این دورهٔ آموزشی مورد استفاده قرار دادهایم، وارد پوشهٔ classes
شده و فایلی به نام SampleTrait1.php
ساخته و آن را به صورت زیر تکمیل میکنیم:
<?php
namespace SokanAcademy;
trait SampleTrait1
{
private $firstname;
public function setFirstname(string $name)
{
$this->firstname = $name;
}
}
همانطور که ملاحظه میشود، پروسهٔ ساخت تِرِیت بسیار مشابه ساخت کلاس است با این تفاوت که به جای کیورد class
از trait
استفاده کرده و نامی دلخواه برای آن در نظر میگیریم. در مثال فوق، یک پراپرتی تحت عنوان firstname$
از جنس private
تعریف کردهایم سپس متدی نوشتهایم به نام ()setFirstname
که یک پارامتر ورودی تحت عنوان name$
میگیرد که میباید دیتا تایپ آن حتماً استرینگ باشد و داخل بدنهٔ این متد نیز مقدار پارامتر ورودی را به پراپرتی firstname$
منتسب نمودهایم. بر همین منوال، داخل پوشهٔ classes
فایل دیگری تحت عنوان SampleTrait2.php
ساخته و آن را به صورت زیر تکمیل میکنیم:
<?php
namespace SokanAcademy;
trait SampleTrait2
{
private $lastname;
public function setLastname(string $surname)
{
$this->lastname = $surname;
}
}
با توجه به این که سازوکار کدهای فوق مشخص است، از تفسیر آنها خودداری میکنیم. در ادامه، فایلی به نام User.php
ساخته و خواهیم دید که به چه شکل میتوان از تِرِیتهای فوق داخل آن استفاده نمود به طوری که داریم:
<?php
namespace SokanAcademy;
class User
{
use SampleTrait1, SampleTrait2;
public function getFullName()
{
return $this->firstname . ' ' . $this->lastname;
}
}
به منظور استفاده از یک تِرِیت، داخل بدنهٔ کلاس دستور use
را نوشته سپس نام تِرِیت مذکور را میآوریم و اگر هم بخواهیم بیش از یک تِرِیت را مورد استفاده قرار دهیم، مابین آنها علامت ,
قرار داده و در نهایت هم یک علامت ;
درج میکنیم. در واقع، وقتی از یک تِرِیت استفاده میکنیم، گویی تمامی پراپرتیها و متدهای آن تِرِیت داخل کلاسمان قرار دارند به طوری که مثلاً داریم:
<?php
namespace SokanAcademy;
class User
{
private $firstname;
private $lastname;
public function setFirstname(string $name)
{
$this->firstname = $name;
}
public function setLastname(string $surname)
{
$this->lastname = $surname;
}
public function getFullName()
{
return $this->firstname . ' ' . $this->lastname;
}
}
در واقع، پس از استفاده از دو تِرِیت فوق در کلاس User
، ساختار این کلاس به صورت فوق خواهد بود. مجدد کلاس User
را به حالت قبل باز میگردانیم:
<?php
namespace SokanAcademy;
class User
{
use SampleTrait1, SampleTrait2;
public function getFullName()
{
return $this->firstname . ' ' . $this->lastname;
}
}
علاوه بر متدهایی که داخل تِرِیتهای SampleTrait1
و SampleTrait2
تعریف کردهایم، داخل خود این کلاس نیز یک متد تعریف کردهایم تحت عنوان ()getFullName
که این وظیفه را دارا است تا مقادیر پراپرتیهای firstname$
و lastname$
را ریترن کند. حال به منظور تست عملکرد این کلاس، وارد فایل index.php
شده و آن را به صورت زیر تکمیل میکنیم:
<?php
ini_set('display_errors', '1');
require_once 'vendor/autoload.php';
$user = new SokanAcademy\User();
$user->setFirstname("Behzad");
$user->setLastname("Moradi");
echo $user->getFullName();
با توجه به این که قبلاً گفتیم در صورت استفاده از یک تِرِیت گویی کلیهٔ پراپرتیها و متدهای آن داخل کلاس مد نظرمان وجود دارند، از همین روی داخل این فایل صرفاً آبجکتی از روی کلاس User
ساخته و متدهای ()setFirstname
و ()setLastname
را با در نظر گرفتن آرگومانهایی از جنس استرینگ فراخوانی نموده و در نهایت با استفاده از دستور echo
مقدار بازگشتی متد ()getFullName
را چاپ کردهایم:
Behzad Moradi
میبینیم که به درستی مقادیر پراپرتیهای firstname$
و lastname$
چاپ شدهاند.
جمعبندی
اساساً میتوان گفت که کاربردهای Abstract Class ،Interface و Trait همپوشانی دارند به طوری که اگر فقط و فقط بخواهیم شناسهٔ متدهای داخل یک کلاس را تعریف کنیم، میباید از اینترفیسها استفاده نماییم اما اگر بخواهیم علاوه بر تعریف یک سری شناسهٔ متد، متدهایی هم با ساختار کامل داشته باشیم میتوانیم از کلاسهای اَبستِرکت استفاده نماییم و اگر هم یک سری متد داشته باشیم که بارها و بارها در جایجای سورسکد مورد استفاده قرار میگیرند، میتوان آنها را در قالب یک سری تِرِیت تعریف نموده و داخل هر کلاسی که به آن گروه از متدها نیاز داشتیم، آن تِرِیت خاص را use
کنیم.