سرفصل‌های آموزشی
آموزش OOP در PHP
آشنایی با مفهوم Trait در زبان PHP

آشنایی با مفهوم Trait در زبان PHP

پیش از این گفتیم که در برخی زبان‌های برنامه‌نویسی شیئ‌گرا همچون 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 کنیم.

online-support-icon