yii2行爲和Trait

這多是你們最疑問的地方,到底有和不一樣。php

進入咱們本身的解釋以前,我先把yii2官方的說法粘貼一份過來,這是一個咱們在選擇用行爲仍是trait的一個標準。html

官方的說明

儘管行爲在 "注入" 屬性和方法方面相似於 trait ,它們在不少方面卻不相同。如上所述,它們各有利弊。它們更像是互補的而不是相互替代。yii2

行爲的優點

  • 行爲類像普通類支持繼承。另外一方面,trait 能夠視爲 PHP 語言支持的複製粘貼功能,它不支持繼承。app

  • 行爲無須修改組件類就可動態附加到組件或移除。要使用 trait,必須修改使用它的類。yii

  • 行爲是可配置的而 trait 不能。函數

  • 行爲以響應事件來自定義組件的代碼執行。spa

  • 當不一樣行爲附加到同一組件產生命名衝突時,這個衝突經過先附加行爲的優先權自動解決。而由不一樣 trait 引起的命名衝突須要經過手工重命名衝突屬性或方法來解決。code

trait 的優點

  • traits 比起行爲更高效,由於行爲是對象,消耗時間和內存。component

  • IDE 對 trait 更友好,由於它們是語言結構。cdn

  • 行爲只能服務於組件類,而 trait 沒有這個限制。

總結一下

經過上面的說明,總結來講就一條:用行爲、用行爲、用行爲。

-------------------我是分割線------------------------

雖然咱們已經會了行爲,官方也說你就用行爲就好了。可是咱們仍是有必要了解下traits,它究竟是什麼?

traits的注入

首先要說的是traits的目的是解決類只能單繼承的問題,行爲也是這個目的,咱們在第一篇前導課 - 什麼是行爲?就已經分析了 。

有一點很重要,就是trait不能被實例化。

咱們來舉一個 trait 的例子,讓你知道什麼是trait

trait Mouse {
    public $name = '鼠標';
    public function click() {
        echo "鼠標點擊了一下";
    }
}

class Computer {
    public function sayName(){
        echo "我是一臺電腦";
    }
}

class Macbook extends Computer {
    use Mouse;
    public function say(){
        echo "我是一條有逼格的macbook";
    }
}複製代碼

我來解釋一下上面的代碼,Macbook 和 Computer 是繼承關係,Mouse 是一個trait ,Macbook 使用 use 關鍵字將 Mouse 注入到本身體內,此刻 Macbook 的 use 有點像yii2的 behaviors 函數。

接下來看看使用狀況

$model= new Macbook ();
$model->say();// 我是一條有逼格的macbook
$model->sayName(); // 我是一臺電腦
$model->click(); // 鼠標點擊了一下複製代碼

效果不錯
效果不錯

很高興的是 trait 也一樣注入了 Macbook 類加強了其功能,可是和行爲不一樣是咱們必須經過修改 Macbook 類代碼實現,而yii2的行爲能夠經過動態綁定來實現對 Macbook 類的零修改。關於動態綁定能夠參考以前乾貨文章 傳送門

優先級

這個問題在行爲和 trait 中都會存在,當使用它們的類、繼承的類中具備相同成員時誰會被使用?

  • 對於trait,優先順序是來自當前類的成員覆蓋了 trait 的方法,而 trait 則覆蓋了被繼承的方法。
  • 對於行爲,優先順序是來自當前類的成員覆蓋了被繼承的方法,而繼承的方法則覆蓋了行爲的方法。

沒明白麼?那看個人這個圖。

alt
alt

在順序上略有不一樣。

多個組合成員名稱衝突問題

什麼意思那,就是一個類同時使用了多個 trait 的狀況,看下面代碼。

trait Mouse {
    public function click() {
        echo "鼠標點擊了一下";
    }
}

trait Keyboard{
    public function click() {
        echo "鍵盤點擊了一下";
    }
}

class Macbook {
    use Mouse,Keyboard;
}

$model = new Macbook();
$model->click();複製代碼

咱們看看結果

哎

很不幸,對於此種情景PHP並無給出自動處理方案,直接拋出了致命error。

這個時候咱們必須人工參與,使用下面的關鍵詞

  • insteadof
  • as

咱們來講明下,仍是上面的例子

...
class Macbook {
    use Mouse,Keyboard {
        Mouse::click insteadof Keyboard;
    }
}

$model = new Macbook();
$model->click();複製代碼

結果輸出了 「鼠標點擊了一下」,由於咱們use的時候告訴php Mouse::click insteadof Keyboard,當衝突的時候使用Mouse的click代替Keyboard的同名方法。

固然咱們還可使用別名as,注意as沒法替代insteadof,可是它可讓被替代者以其餘的名字運行。

...
class Macbook {
    use Mouse,Keyboard {
        Mouse::click insteadof Keyboard;
        Keyboard::click as keyClick;
    }
}

$model = new Macbook();
$model->click();
$model->keyClick();複製代碼

看看結果

alt
alt

咱們解決了衝突。

yii2的多行爲成員衝突問題

而對於yii2而言則按照先附加行爲的優先權自動解決,並不會產生錯誤,具體如何解決本篇再也不重複,能夠去看下 揭祕yii2中行爲的方法是如何注入到組件類中去的~ 這篇。

固然這種衝突解決方法會將後面的同名函數雪藏,而 trait 則能夠將其經過as關鍵詞再次啓動起來。可是這個問題並不大,此種情景咱們能夠有太多方法解決了。

關於事件

event是個不能忽視的存在,經過前幾篇咱們都知道行爲+事件能夠擁有不少炫酷的注入能力,那麼trait ?

固然也能夠,可是會麻煩不少,我來舉個例子。

// @app\components\EventTrait.php
namespace app\components;

use app\models\User;

trait EventTrait {
    public function initEvent(){
        User::on(User::EVENT_LOOK_ME,function(){
            echo "i am event";
        });
    }
}

// @app\models\User.php
class User extends \yii\db\ActiveRecord {
    ...
    const EVENT_LOOK_ME = 'event_look_me';

    use EventTrait;
    ...
}

// 在action中
$model = new User();
$model->initEvent();
$model->trigger(User::EVENT_LOOK_ME);複製代碼

雖然也能夠實現,可是天然沒有行爲來得容易,對代碼的侵入性也太強,不建議這樣用。

其餘

固然根據官方還有不少,好比行爲可配置等,這些由於前幾篇都已經進行了講解,這裏再也不重複。

總體來講,yii2行爲和trait 均可以解決單繼承問題,而且也具備很不錯的注入和組合能力,可是在各方面行爲和yii2其餘模塊整合更好一點,所以推薦用yii2的行爲。

不過這不是說你能夠放棄 trait 了,記住,行爲只能服務於組件類,可是yii2的世界裏還有不是組件類的孩子們,請思考。

最後說一句,今天七夕,祝兄弟連全部有媳婦的、有媳婦的節日快樂,沒媳婦的趕忙code。


相關文章
相關標籤/搜索