這可能是大家最疑問的地方,到底有和不同。
進入我們自己的解釋之前,我先把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 則覆蓋了被繼承的方法。
- 對於行為,優先順序是來自當前類的成員覆蓋了被繼承的方法,而繼承的方法則覆蓋了行為的方法。
沒明白麼?那看我的這個圖。
在順序上略有不同。
多個組合成員名稱衝突問題
什麼意思那,就是一個類同時使用了多個 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();複製程式碼
看看結果
我們解決了衝突。
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。