yii2中行為和Trait的區別
這可能是大家最疑問的地方,到底有和不同。
進入我們自己的解釋之前,我先把yii2官方的說法粘貼一份過來,這是一個我們在選擇用行為還是trait的一個標準。
官方的說明
儘管行為在 "注入" 屬性和方法方面類似於 trait ,它們在很多方面卻不相同。如上所述,它們各有利弊。它們更像是互補的而不是相互替代。
行為的優勢
行為類像普通類支持繼承。另一方面,trait 可以視為 PHP 語言支持的複製粘貼功能,它不支持繼承。
行為無須修改組件類就可動態附加到組件或移除。要使用 trait,必須修改使用它的類。
行為是可配置的而 trait 不能。
行為以響應事件來自定義組件的代碼執行。
當不同行為附加到同一組件產生命名衝突時,這個衝突通過先附加行為的優先權自動解決。而由不同 trait 引發的命名衝突需要通過手工重命名衝突屬性或方法來解決。
trait 的優勢
traits 比起行為更高效,因為行為是對象,消耗時間和內存。
IDE 對 trait 更友好,因為它們是語言結構。
行為只能服務於組件類,而 trait 沒有這個限制。
總結一下
通過上面的說明,總結來說就一條:用行為、用行為、用行為。
雖然我們已經會了行為,官方也說你就用行為就行了。但是我們還是有必要了解下traits,它到底是什麼?
traits的注入
首先要說的是traits的目的是解決類只能單繼承的問題,行為也是這個目的,我們在第一篇 前導課 - 什麼是行為? 就已經分析了 。
有一點很重要,就是trait不能被實例化。
我們來舉一個 trait 的例子,讓你知道什麼是trait
traitMouse {public$name = 滑鼠 ;publicfunctionclick(){echo"滑鼠點擊了一下"; }}classComputer{publicfunctionsayName(){echo"我是一台電腦"; }}classMacbookextendsComputer{useMouse;publicfunctionsay(){echo"我是一條有逼格的macbook"; }}
我來解釋一下上面的代碼,Macbook 和 Computer 是繼承關係,Mouse 是一個trait ,Macbook 使用 use 關鍵字將 Mouse 注入到自己體內,此刻 Macbook 的 use 有點像yii2的 behaviors 函數。
接下來看看使用情況
$model=newMacbook ();$model->say();// 我是一條有逼格的macbook$model->sayName();// 我是一台電腦$model->click();// 滑鼠點擊了一下
很高興的是 trait 也同樣注入了 Macbook 類增強了其功能,但是和行為不同是我們必須通過修改 Macbook 類代碼實現,而yii2的行為可以通過動態綁定來實現對 Macbook 類的零修改。關於動態綁定可以參考之前乾貨文章 傳送門
優先順序
這個問題在行為和 trait 中都會存在,當使用它們的類、繼承的類中具有相同成員時誰會被使用?
對於trait,優先順序是來自當前類的成員覆蓋了 trait 的方法,而 trait 則覆蓋了被繼承的方法。
對於行為,優先順序是來自當前類的成員覆蓋了被繼承的方法,而繼承的方法則覆蓋了行為的方法。
沒明白么?那看我的這個圖。
在順序上略有不同。
多個組合成員名稱衝突問題
什麼意思那,就是一個類同時使用了多個 trait 的情況,看下面代碼。
traitMouse {publicfunctionclick(){echo"滑鼠點擊了一下"; }}traitKeyboard{publicfunctionclick(){echo"鍵盤點擊了一下"; }}classMacbook{useMouse,Keyboard;}$model =newMacbook();$model->click();
我們看看結果
很不幸,對於此種情景PHP並沒有給出自動處理方案,直接拋出了致命error。
這個時候我們必須人工參與,使用下面的關鍵詞
insteadof
as
我們來說明下,還是上面的例子
...classMacbook{useMouse,Keyboard{Mouse::clickinsteadofKeyboard; }}$model =newMacbook();$model->click();
結果輸出了 「滑鼠點擊了一下」,因為我們use的時候告訴phpMouse::click insteadof Keyboard,當衝突的時候使用Mouse的click代替Keyboard的同名方法。
當然我們還可以使用別名as,注意as無法替代insteadof,但是它可以讓被替代者以其他的名字運行。
...classMacbook{useMouse,Keyboard{Mouse::clickinsteadofKeyboard; Keyboard::clickaskeyClick; }}$model =newMacbook();$model->click();$model->keyClick();
看看結果
我們解決了衝突。
yii2的多行為成員衝突問題
而對於yii2而言則按照先附加行為的優先權自動解決,並不會產生錯誤,具體如何解決本篇不再重複,可以去看下 揭秘yii2中行為的方法是如何注入到組件類中去的~ 這篇。
當然這種衝突解決方法會將後面的同名函數雪藏,而 trait 則可以將其通過as關鍵詞再次啟動起來。但是這個問題並不大,此種情景我們可以有太多方法解決了。
關於事件
event是個不能忽視的存在,通過前幾篇我們都知道行為+事件可以擁有很多炫酷的注入能力,那麼trait ?
當然也可以,但是會麻煩很多,我來舉個例子。
// @appcomponentsEventTrait.phpnamespaceappcomponents;useappmodelsUser;traitEventTrait {publicfunctioninitEvent(){ User::on(User::EVENT_LOOK_ME,function(){echo"i am event"; }); }}// @appmodelsUser.phpclassUserextendsyiidbActiveRecord{ ...constEVENT_LOOK_ME = event_look_me ;useEventTrait; ...}// 在action中$model =newUser();$model->initEvent();$model->trigger(User::EVENT_LOOK_ME);
雖然也可以實現,但是自然沒有行為來得容易,對代碼的侵入性也太強,不建議這樣用。
其他
當然根據官方還有很多,比如行為可配置等,這些因為前幾篇都已經進行了講解,這裡不再重複。
整體來說,yii2行為和trait 都可以解決單繼承問題,並且也具有很不錯的注入和組合能力,但是在各方面行為和yii2其他模塊整合更好一點,因此推薦用yii2的行為。
不過這不是說你可以放棄 trait 了,記住,行為只能服務於組件類,但是yii2的世界裡還有不是組件類的孩子們,請思考。
※關於PHP的錯誤機制總結
※SecLists-安全測試者的手冊
※2017 最新 PHP 框架橫向對比
※我的開發工具集們
※PHP實現queue數據結構
TAG:PHP技術大全 |