PHP 中 Trait 詳解及其應用
從PHP的5.4.0版本開始,PHP提供了一種全新的程式碼複用的概念,那就是Trait。Trait其字面意思是”特性”、”特點”,我們可以理解為,使用Trait關鍵字,可以為PHP中的類新增新的特性。
熟悉物件導向的都知道,軟體開發中常用的程式碼複用有繼承和多型兩種方式。在PHP中,只能實現單繼承。而Trait則避免了這點。下面通過簡單的額例子來進行對比說明。
1. 繼承 VS 多型 VS Trait
現在有Publish.php
和Answer.php
這兩個類。要在其中新增LOG功能,記錄類內部的動作。有以下幾種方案:
- 繼承
- 多型
- Trait
1.1. 繼承
如圖:
程式碼結構如下:
// Log.php <?php Class Log { public function startLog() { // echo ... } public function endLog() { // echo ... } }
// Publish.php <?php Class Publish extends Log { }
// Answer.php <?php Class Answer extends Log { }
可以看到繼承的確滿足了要求。但這卻違背了物件導向的原則。而釋出(Publish)和回答(Answer)這樣的操作和日誌(Log)之間的關係並不是子類與父類的關係。所以不推薦這樣使用。
1.2. 多型
如圖:
實現程式碼:
// Log.php <?php Interface Log { public function startLog(); public function endLog(); }
// Publish.php <?php Class Publish implements Log { public function startLog() { // TODO: Implement startLog() method. } public function endLog() { // TODO: Implement endLog() method. } }
// Answer.php <?php Class Answer implements Log { public function startLog() { // TODO: Implement startLog() method. } public function endLog() { // TODO: Implement endLog() method. } }
記錄日誌的操作應該都是一樣的,因此,釋出(Publish)和回答(Answer)動作中的日誌記錄實現也是一樣的。很明顯,這違背了DRY(Don’t Repeat Yourself)原則。所以是不推薦這樣實現的。
1.3. Trait
如圖:
實現程式碼如下:
// Log.php <?php trait Log{ public function startLog() { // echo .. } public function endLog() { // echo .. } }
// Publish.php <?php class Publish { use Log; } $publish = new Publish(); $publish->startLog(); $publish->endLog();
// Answer.php <?php class Answer { use Log; } $answer = new Answer(); $answer->startLog(); $answer->endLog();
可以看到,我們在沒有增加程式碼複雜的情況下,實現了程式碼的複用。
1.4. 結論
繼承的方式雖然也能解決問題,但其思路違背了物件導向的原則,顯得很粗暴;多型方式也可行,但不符合軟體開發中的DRY原則,增加了維護成本。而Trait方式則避免了上述的不足之處,相對優雅的實現了程式碼的複用。
2. Trait的作用域
瞭解了Trait的好處,我們還需要了解其實現中的規則,先來說一下作用域。這個比較好證明,實現程式碼如下:
<?php class Publish { use Log; public function doPublish() { $this->publicF(); $this->protectF(); $this->privateF(); } } $publish = new Publish(); $publish->doPublish();
執行上述程式碼輸出結果如下:
public function protected function private function
可以發現,Trait的作用域在引用該Trait類的內部是都可見的。可以理解為use關鍵字將Trait的實現程式碼Copy了一份到引用該Trait的類中。
3. Trait中屬性的優先順序
說到優先順序,就必須要有一個對比的參照物,這裡的參照物件時引用Trait的類及其父類。
通過以下的程式碼來證明Trait應用中的屬性的優先順序:
<?php trait Log { public function publicF() { echo __METHOD__ . ' public function' . PHP_EOL; } protected function protectF() { echo __METHOD__ . ' protected function' . PHP_EOL; } } class Question { public function publicF() { echo __METHOD__ . ' public function' . PHP_EOL; } protected function protectF() { echo __METHOD__ . ' protected function' . PHP_EOL; } } class Publish extends Question { use Log; public function publicF() { echo __METHOD__ . ' public function' . PHP_EOL; } public function doPublish() { $this->publicF(); $this->protectF(); } } $publish = new Publish(); $publish->doPublish();
上述程式碼的輸出結果如下:
Publish::publicF public function Log::protectF protected function
通過上面的例子,可以總結出Trait應用中的優先順序如下:
- 來自當前類的成員覆蓋了 trait 的方法
- trait 覆蓋了被繼承的方法
類成員優先順序為:當前類>Trait>父類
4. Insteadof和As關鍵字
在一個類中,可以引用多個Trait,如下:
<?php trait Log { public function startLog() { echo __METHOD__ . ' public function' . PHP_EOL; } protected function endLog() { echo __METHOD__ . ' protected function' . PHP_EOL; } } trait Check { public function parameterCheck($parameters) { // do sth } } class Publish extends Question { use Log,Check; public function doPublish($para) { $this->startLog(); $this->parameterCheck($para); $this->endLog(); } }
通過上面的方式,我們可以在一個類中引用多個Trait。引用多個Trait的時候,就容易出問題了,最常見的問題就是兩個Trait中如果出現了同名的屬性或者方法該怎麼辦呢?這個時候就需要用到Insteadof
和 as
這兩個關鍵字了.請看如下實現程式碼:
<?php trait Log { public function parameterCheck($parameters) { echo __METHOD__ . ' parameter check' . $parameters . PHP_EOL; } public function startLog() { echo __METHOD__ . ' public function' . PHP_EOL; } } trait Check { public function parameterCheck($parameters) { echo __METHOD__ . ' parameter check' . $parameters . PHP_EOL; } public function startLog() { echo __METHOD__ . ' public function' . PHP_EOL; } } class Publish { use Check, Log { Check::parameterCheck insteadof Log; Log::startLog insteadof Check; Check::startLog as csl; } public function doPublish() { $this->startLog(); $this->parameterCheck('params'); $this->csl(); } } $publish = new Publish(); $publish->doPublish();
執行上述程式碼,輸出結果如下:
Log::startLog public function Check::parameterCheck parameter checkparams Check::startLog public function
就如字面意思一般,insteadof
關鍵字用前者取代了後者,as
關鍵字給被取代的方法起了一個別名。
在引用Trait時,使用了use關鍵字,use關鍵字也用來引用名稱空間。兩者的區別在於,引用Trait時是在class內部使用的。
相關文章
- PHP中Trait特性PHPAI
- 設計模式 - 命令模式詳解及其在JdbcTemplate中的應用設計模式JDBC
- 設計模式 - 迭代器模式詳解及其在ArrayList中的應用設計模式
- PHP TraitPHPAI
- PHP中Trait的使用總結PHPAI
- PHP中的trait方法衝突PHPAI
- Java 註解及其在 Android 中的應用JavaAndroid
- Android中SQLite應用詳解AndroidSQLite
- PHP之TraitPHPAI
- 說說 PHP 的魔術方法及其應用PHP
- PHP中php.ini配置詳解PHP
- PHP 觀察者模式應用場景例項詳解PHP模式
- PHP 核心特性 - TraitPHPAI
- 數位電路-D觸發器詳解及其在編碼器解碼電路中的應用觸發器
- Tarjan演算法及其應用 總結+詳細講解+詳細程式碼註釋演算法
- 關於C++中物件與類的詳解及其作用詳解C++物件
- php中declare的作用詳解PHP
- 【PHP】啟用php-fpm狀態詳解PHP
- PHP 特性之 trait (一)PHPAI
- ctags 處理 PHP traitPHPAI
- Ajax及其應用
- iOS開發中的Scroll View應用詳解iOSView
- Android應用中Clean架構使用詳解Android架構
- PHP trait 特性在 Laravel 中的使用個人心得PHPAILaravel
- JavaScript中的bind方法及其常見應用JavaScript
- AOP及其在Spring中的應用(一) .Spring
- AOP及其在Spring中的應用(二)Spring
- php當中的memcache應用PHP
- PHP 中 CURL 使用之 CURL 詳解!PHP
- php中Session使用方法詳解PHPSession
- php中curl的詳細解說PHP
- PHP 中的設計模式詳解PHP設計模式
- PHP中return用法詳細解讀PHP
- PHP中的traits使用詳解PHPAI
- php中mysql操作buffer用法詳解PHPMySql
- vmstat的應用詳解
- 泛型及其應用泛型
- 閉包及其應用