PHP Traits代碼複用方法淺析

       在看Yii的文檔,Yii2用到了一個叫Traits的東西,查詢文檔可以知道自PHP5.4起,PHP實現了一個代碼複用的方法,就是traits。官方的解釋一大堆,翻譯過來的晦澀難懂,我理解的意思就是——PHP是單繼承語言,爲了在不同結構的類裏面複用一些方法,且不必像多繼承類那樣複雜,就使用了Traits來解決這類問題。簡而言之,traits就是實現代碼複用的方法。

1.使用簡介

使用起來很簡單,這個方法的官方名字是Traits,使用的時候是trait:

trait zz{
	function showMe(){
		echo "show me something in trails";
	}
}
class ZzTest{
	use zz;
}

$test = new ZzTest();
$test->showMe();
這裏最終會打印出:show me something in trails

從基類繼承的子類裏使用traits的,會有一個覆蓋的問題,我們看下面的代碼。

trait zz{
	function showMe(){
		parent::showMe();
		echo "show me something in trails";
	}
}
class Father{
	function showMe(){
		echo "show me in Father<br/>";
	}
}
class ZzTest extends Father{
	use zz;
}
$test = new ZzTest();
$test->showMe();

最終會打印出:

show me in Father
show me something in trails

trait中的方法覆蓋了父類的方法,所以執行的是trait中的showMe(),這裏可以理解成子類中得到了trait作爲自己的方法,覆蓋了父類的方法。然後再看下面的代碼:

trait zz{
	function showMe(){
		parent::showMe();
		echo "show me something in trails";
	}
}
class Father{
	function showMe(){
		echo "show me in Father<br/>";
	}
}
class ZzTest extends Father{
	use zz;
	public function showMe(){
		echo "show me MySelf";
	}
}
$test = new ZzTest();
$test->showMe();
結果會打印出:show me MySelf

子類中會覆蓋Trait中的方法,優先級關係爲子類 > trait> 基類。



2.多個trait

use聲明時,列出多個trait,可以都插入到一個類中。

trait zz{
	function sayMe(){
		echo "Say me.";
	}
}
trait nn{
	function sayYou(){
		echo "Say you,";
	}
}
class ZzTest{
	use zz,nn;
	public function sayIt(){
		echo "Say it for always.";
	}
}
$test = new ZzTest();
$test->sayYou();
$test->sayMe();
$test->sayIt();
結果打印出:Say you,Say me.Say it for always.

如果trait中有重名的方法,會造成衝突:

trait zz{
	function sayMe(){
		echo "Say me.";
	}
	function sayYou(){
		echo "sAY YOU,";
	}
}
trait nn{
	function sayYou(){
		echo "Say you,";
	}
	function sayMe(){
		echo "sAY ME";
	}
}
class ZzTest{
	use zz,nn;
	public function sayIt(){
		echo "Say it for always.";
	}
}
 最終會報錯:Fatal error: Trait method sayYou has not been applied, because there are collisions with other trait methods on ZzTest

   解決這個問題可以將同名的方法指定給另一個trait。

trait zz{
	function sayMe(){
		echo "Say me.";
	}
	function sayYou(){
		echo "sAY YOU,";
	}
}
trait nn{
	function sayYou(){
		echo "Say you,";
	}
	function sayMe(){
		echo "sAY ME.";
	}
}
class ZzTest{
	use zz,nn{
		nn::sayYou insteadof zz;
		zz::sayMe insteadof nn; 
	}
	public function sayIt(){
		echo "Say it for always.";
	}
}
$test = new ZzTest();
$test->sayYou();
$test->sayMe();
$test->sayIt();
指定使用trait nn中的sayYou(),和zz中的sayMe();最終打印出來也是:Say you,Say me.Say it for always.這裏是指定使用哪一個,如果也想用另一個trait中的同名方法,就可以用as操作符將重名方法起一個別名,在之後的調用中使用別名。

use zz,nn{
		nn::sayYou insteadof zz;
		zz::sayMe insteadof nn; 
		zz::sayYou as sayYounn;
		nn::sayMe as asMenn;
	}
   起了別名之後在之後就可以調用他們

$test->sayYouzz();
$test->sayMenn();
//打印結果:sAY YOU,sAY ME.



3.方法的訪問控制

trait默認的訪問控制是public,也可以修改。這裏需要知道一點,trait裏的代碼可以看作是孤立的代碼段,然後拿這些放到類裏面去用。類裏面是如何定義的,這裏就可以怎麼寫。

trait zz{
	public function sayYou(){
		$this->sayMe();
	}
	protected function sayMe(){
		echo "Say me.";
	}
}

class ZzTest{
	use zz;
	public function sayIt(){
		echo "Say it for always.";
	}
}
$test = new ZzTest();
$test->sayYou();
像這樣是可行的,打印出:Say me.


用as就可以修改訪問控制:

trait zz{
	protected function sayMe(){
		echo "Say me.";
	}
}
class ZzTest{
	use zz{
		sayMe as public;
	}
	public function sayIt(){
		echo "Say it for always.";
	}
}
$test = new ZzTest();
$test->sayMe();


4.trait組成trait

正如類能夠使用 trait 一樣,其它 trait 也能夠使用 trait。在 trait 定義時通過使用一個或多個 trait,它能夠組合其它 trait 中的部分或全部成員。

trait zz{
	function sayMe(){
		echo "Say me.";
	}
}
trait nn{
	function sayYou(){
		echo "Say you,";
	}
}
trait wj{
	use zz,nn;
}
class ZzTest{
	use wj;
}
$test = new ZzTest();
$test->sayYou();
$test->sayMe();
最後也是一樣打印出:Say you,Say me.


5.trait的抽象成員

爲了對使用trait的類施加強制要求,trait 支持抽象方法的使用。

trait zz{
	function sayYou(){
		echo "Say you,".$this->sayMe();
	}
	abstract public function sayMe();
}

class ZzTest{
	private $song = "Say me.";
	use zz;
	public function sayMe(){
		return $this->song;
	}
}
$test = new ZzTest();
$test->sayYou();
//結果依然是打印出:Say you,Say me.

6.其他特性

Traits 可以被靜態成員靜態方法定義,也可以定義屬性。

trait zz{
	public $name = 'Say it for always.<br/>';
	function sayYou(){
        static $song = 'Say you,';
        echo $song.'Say me.';
    }
    public static function sing(){
    	echo 'That\'s the way it should be.';
    }

}

class ZzTest{
	use zz;
}
$test = new ZzTest();
$test->sayYou();
echo $test->name;
$test->sing();

最後打印結果送給你:

Say you,Say me.Say it for always.
That's the way it should be.








發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章