yii2行為和Trait

阿北哥ya發表於2017-08-28

這可能是大家最疑問的地方,到底有和不同。

進入我們自己的解釋之前,我先把yii2官方的說法貼上一份過來,這是一個我們在選擇用行為還是trait的一個標準。

官方的說明

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

行為的優勢

  • 行為類像普通類支援繼承。另一方面,trait 可以視為 PHP 語言支援的複製貼上功能,它不支援繼承。

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

  • 行為是可配置的而 trait 不能。

  • 行為以響應事件來自定義元件的程式碼執行。

  • 當不同行為附加到同一元件產生命名衝突時,這個衝突通過先附加行為的優先權自動解決。而由不同 trait 引發的命名衝突需要通過手工重新命名衝突屬性或方法來解決。

trait 的優勢

  • traits 比起行為更高效,因為行為是物件,消耗時間和記憶體。

  • IDE 對 trait 更友好,因為它們是語言結構。

  • 行為只能服務於元件類,而 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。


相關文章