TimTim

سایت خبر خوان

همه ی اون چیزی که در مورد php trait باید بدونیم

دوشنبه 26 فروردین 98 | 11:37 - virgool.io - 32
قطعه پازل تکمیل کننده ارث بری در زبان های single inheritance یعنی trai...
php trait
php trait

ویژگی های عالی trait ها در ارث بری، اونها رو به المان های پرکاربرد زبان php تبدیل کرده است.

این پست رو نوشتم تا تمام موارد کاربردی trait ها رو یک جا بتونیم بخونیم و در زمان طراحی ساختار برنامه oop خودمون و یا refactor کردن اونها از trait ها استفاده کنیم.

ابتدا تعریف trait

در واقع trait یک روش ارث بری دیگر در کنار روش ارث بری از کلاس هست، به این معنی که trait ها المان هایی در php هستند که می توان با نوشتن و ارث بری از آنها در کلاس های مختلف اصطلاحا کد های reusable داشته باشیم و چندین و چند باره method های خودمون رو ننویسیم.

چرا به trait نیاز داریم وقتی می توان همین کار را با class انجام داد؟
از آنجایی که php یک زبان single inheritance هست به این معنی که فقط می توان از یک کلاس ارث بری (extends) کرد پس اگر شما دارید یک کلاس جدید می نویسید که به ۲ تا کلاس دیگر به عنوان parent نیاز دارد این مورد در php امکان پذیر نیست. پس چطوری این کار رو انجام دهیم؟؟؟

این جا هست که trait ها به این اندازه پرکاربرد شده اند یعنی جایی که به جای نوشتن class ما trait می نویسیم و میتوان چند تا از trait ها رو در class خودمون use کنیم.

مشاهده syntax ابتدایی trait
همان طور که در کد زیر می بینید trait به سادگی با کلمه trait تعریف می شود و شما مجاز هستید تا انواع property و method ها رو در آن بنویسید.

یعنی دقیقا همان کاری که در نوشتن یک class انجام می دهید اما با این تفاوت که از trait نمی توان instance گرفت و تنها باید در class های دیگر use شود.

در کد زیر یک trait به اسم CreatesApplication ساختیم که یک متد به اسم createApplication دارد :

<?php

trait CreatesApplication
{
    public function createApplication()
    {
        $app = require __DIR__.'/../bootstrap/app.php';
        return $app;
    }
}

حالا می خواهیم از این trait ارث بری کنیم و از متد createApplication استفاده کنیم که به صورت زیر تنها کافی هست در class خودمون use CreatesApplication رو بنویسیم:

<?php

 class TestCase extends BaseTestCase
{
    use CreatesApplication;
}

پس اگر از کلاس TestCase یک object بسازیم می توان متد createApplication را روی اون صدا زد.

موارد پیشرفته تر در trait ها
یکی از مواردی که در تست ها و یا مصاحبه های php مصاحبه کننده ها ترجیح می دهند از مصاحبه شوند بپرسند همین trait هستش پس در ادامه این پست با هم موارد پیچیده تر از trait رو خواهیم دید!

چند trait با متد هم نام

الویت یا precedence در trait ها از آن جایی مهم هست که ما می توانیم چند trait را در کلاس خودمون use کنیم و ممکن هست این trait ها دارای متد ها ی هم اسمی باشند که باید ببینیم کدام یکی از آنها اجرا خواهند شد؟
اگر در چنین وضعیتی باشیم و متد هم نام را روی object صدا بزنیم یک fatal error خواهیم گرفت :|
پس باید ضمن اطلاع از این مورد در زمان use کردن این trait ها در کلاس به صورت زیر مشخص کنیم که الویت چگونه رعایت شود و کدام متد صدا زده شود:

<?php
trait A {
    public function smallTalk() {
        echo 'a';
    }
    public function bigTalk() {
        echo 'A';
    }
}

trait B {
    public function smallTalk() {
        echo 'b';
    }
    public function bigTalk() {
        echo 'B';
    }
}

در کد بالا هر دو trait دارای متدهای smallTalk و bigTalk هستند پس در کد زیر وقتی از آنها use می کنم به php می گوییم که اگر smallTalk رو خواستی از B استفاده کن و اگر bigTalk رو خواستی از A استفاده کنم و اینجوری به خطا نخواهیم خورد:

class Talker {
    use A, B {
 B::smallTalk insteadof A;
 A::bigTalk insteadof B;
    }
}


متد هم نام در trait و class

اگر در مثال بالا در خود کلاس Talker هم متد bigTalk رو نوشته بودیم چه اتفاقی می افتاد؟

در اینجا php بدون اینکه error بدهد می رود و از متد خود class استفاده می کند و به این صورت ما متد bigTalk رو در trait از دست می دهیم.

حالا فرض کنیم ما به متد bigTalk از ‌B هم نیاز داریم پس در این صورت باید این متد رو به یک اسم دیگر به class خودمون اضافه کنیم که خواهیم داشت:

class Aliased_Talker {
    use A, B {
 B::smallTalk insteadof A;
 A::bigTalk insteadof B;
 B::bigTalk as talk;
    }

    public function bigTalk() { 
                echo 'bigTalk';     
   }
}

حالا اگر یک object از کلاس Aliased_Talker بسازیم و talk رو روی اون صدا بزنیم در واقع متد bigTalk از B رو اجرا می کنیم.


ساخت یک trait از trait های دیگر

با این ویژگی می توانیم به جای استفاده از چند trait آنها رو در یک trait داشته باشیم و یک trait جدید بسازیم و از اون داخل class خودمون استفاده کنیم:

<?php
trait Hello {
    public function sayHello() {
        echo 'Hello ';
    }
}

trait World {
    public function sayWorld() {
        echo 'World!';
    }
}

trait HelloWorld {
    use Hello, World;
}

class MyHelloWorld {
    use HelloWorld;
}

در این مثال ۲ تا trait با اسم های Hello و World داشتیم و با استفاده از آنها یک trait به اسم HelloWorld ساختیم و وقتی که HelloWorld رو داخل کلاس MyHelloWorld استفاده کردیم در واقع هر دو trait های Hello و World رو خواهیم داشت.

در کد زیر استفاده از کلاس بالا رو می بینیم:

$o = new MyHelloWorld();
$o->sayHello();
$o->sayWorld();
//output-> Hello World!


تغییر visibility متد های trait در زمان استفاده از آن
فرض کنید از یک trait در کلاس خودتون استفاده می کنید اما نمی خواهید متد شما همان visibility را داشته باشد که در trait هست.

برای مثال در کد زیر ما می خواهیم که متد sayHello به صورت protected باشد و نه public تا فقط در کلاس و child های این کلاس بتوانند آن را صدا بزنند پس طبق کد زیر این کار رو انجام می هیم:

<?php
trait HelloWorld {
    public function sayHello() {
        echo 'Hello World!';
    }
}
// Change visibility of sayHello
class MyClass1 {
    use HelloWorld { sayHello as protected; }
}


نوشتن abstract در trait
در نوشتن trait ها ما می توانیم متد های abstract بنویسیم تا مانند abstract class آنها را در زمان استفاده از trait پیاده سازی کنیم:

<?php
trait Hello {
    public function sayHelloWorld() {
        echo 'Hello'.$this->getWorld();
    }
    abstract public function getWorld();
}

حالا اگر از Hello در کلاس خودمون استفاده کنیم باید متد getWorld رو پیاده سازی کنیم:)

موارد دیگر از ویژگی های trait ها
در trait ها علاوه بر method می توان property هم داشت
در trait ها می توانیم static members و یا static methods داشته باشیم
با توجه به ویژگی هایی که دیدیم trait ها برای refactor کردن کلاس های پیچیده که تعداد متد ها ی اونها خیلی زیاد شده است عالی خواهند بود

این ها موارد ی بود که باید تو ذهنمون باشه تا تو طراحی ها و کد نویسی هامون بهتر و بیشتر از trait ها استفاده کنیم چون همون طوری که دیدیم واقعا برای ارث بری المان های خیلی خوب و قوی به نظر می رسند


امیدوارم این پست برای شما مفید باشه:)

ویرگول,برنامه نویسی,trait,php,refactor,ارث بری در php,