天天看點

trait特性,什麼是trait,整理版

作為PHP5.4引入新特性trait,有不少讀者感覺比較陌生。

本篇從背景、作用、使用三個方面分析trait,力求以最通俗的語言解釋清楚。

背景

PHP在面向對象的設計時, 被設計為單繼承. 什麼叫單繼承, 一個類A隻能繼承一個類B, 再由B類繼承C類. 而不能A類同時繼承B類和C類.

正确的寫法:demo1.php

<?php

class C{}

class B extends C{}

class A extends B{}

$obj = new A;

var_dump($obj);

           

會得到一個對象

/home/vagrant/Code/blog/demo1.php:11:
object(A)[1]      

//注意

//A類同時繼承B類和C類, 這樣寫是不允許的

例如:

<?php

class C{}
class B{}

class A extends B,C{}

$obj = new A;

var_dump($obj);


//代碼是錯誤的。
( ! ) Parse error: syntax error, unexpected token ",", expecting "{" in /home/vagrant/Code/blog/demo2.php on line 6
           

說人話, 就是一個兒子隻能有一個父親, 但是反過來呢?一個父親是可以有多個兒子的.

如果想A類既使用B的方法又使用C的方法, PHP提出了接口(Interface). PHP雖然是單繼承, 但是可以多實作. 什麼叫多實作, 一個類A可以同時實作B接口和C接口.(demo3.php)

<?php

interface C{}

interface B{}

class A implements B,C{}

//A類同時實作接口B和接口C, 這樣是允許的


$obj = new A;

var_dump($obj);
           

同樣會得到一個對象

/home/vagrant/Code/blog/demo3.php:11:
object(A)[1]      

作用

關鍵詞: 假多繼承, 代碼複用, 類的包含

現在有這樣一個場景

需求: A類既可以使用B類的方法又可以使用C的方法, A類和B類是繼承關系, 但是B和C沒有繼承關系, 怎麼解決?

方法:A類繼承B類, 同時實作C接口(demo4.php)

<?php

interface C{
    public function ccc();
}

class B{
    public function bbb()
    {
        echo "我是B類的bbb方法<br>";
    }
}

class A extends B implements C{
    public function ccc()
    {
        echo "我是A類,我要實作接口C定義的方法<br>";
    }
}

$obj = new A;
$obj ->ccc();
$obj ->bbb();
~
~
           

強烈建議上面的代碼,敲10遍以上。

(拓展: 接口)

接口不能被執行個體化

接口是為了規範顯現它的子類,以達到統一的目的

接口中隻有:常量和抽象方法

接口裡的所有方法都是抽象方法,不能寫方法體,并且,接口的抽象方法不需要寫abstract。

子類繼承接口的方法,叫 實作,是以說接口是用來實作的(涉及php基礎知識)

如果這個時候有一個D類, 也要繼承B類, 實作C接口, 需要重複的寫這段代碼(demo5.php)

<?php

interface C{
    public function ccc();
}

class B{
    public function bbb()
    {
        echo "我是B類的bbb方法<br>";
    }
}

class A extends B implements C{
    public function ccc()
    {
        echo "我是A類,我要實作接口C定義的方法<br>";
    }
}

class D extends B implements C{
    //這段代碼是重複的
    public function ccc()
    {
        echo "我是D類,我要實作接口C定義的方法<br>";
    }
}
$obj = new A;
$obj ->ccc();
$obj ->bbb();
           

使用

思考: 能不能将這部分重複的代碼定義在一個類中, A類和D類包含這個類呢?

這個時候trait應用而生(demo6.php)

<?php

interface C{
    public function ccc();
}

class B{
    public function bbb()
    {
        echo "我是B類的bbb方法<br>";
    }
}

trait E{
    public function ccc()
    {
        echo "我是trait,我實作了ccc方法<br>";
//      echo "我是trait,我是".__CLASS__."類,我實作了ccc方法<br>";
    }
}

class A extends B implements C{
    use E;
}

class D extends B implements C{
    use E;
}


$obj = new A;
$obj ->ccc();
$obj ->bbb();
           

運作結果如下:

我是trait,我實作了ccc方法

我是B類的bbb方法

上面的代碼可以繼續優化一下

通過上面的用法, 我們就可以這樣總結

  1. trait本質上還是一個類
  2. 接口(interface)規定了方法的定義, trait規定了方法的實作
  3. 可以認為A類D類等等其他類在需要ccc方法的時候包含了trait

綜上, trait是PHP實作多繼承的一種折中的方法, 姑且叫它"假多繼承", 可以認為一個類(A類)包含了另一個類E(trait), 最終目的是為了實作代碼複用.