思考:php中繼承是單繼承,如果某個類有成員要被其他類使用,就需要成為其他類的父類才行,這樣可能會導致繼承鏈會長,合適嗎?
引入:從繼承的角度出發,繼承鏈可以解決問題,但是的確效率會打折扣,同時,如果某些功能是共性使用,但是並不符合繼承條件(不屬於同一類)
那么使用繼承也有所違背面上對象規則,此時可以使用php提供的另外一種代碼復用技術trait
trait代碼復用【掌握】
定義:trait是為類似php的單繼承語言而准備的一種代碼復用機制,trait可以使單繼承語言為了復用而不得不繼承的尷尬,讓面向對象更加純粹
1.trait是一種類似class的關鍵字
<?php // 定義trait trait show{ }
2.trait內部可以像類一樣擁有成員屬性(包含靜態),成員方法(包含靜態),但不能有常量
<?php // 定義trait trait show{ public $name; protected $host; //允許定義,但實際不用 private $info; public function show_time(){ echo $this->info; } protected function showhost(){ //允許定義,但實際不用 echo $this->name; } const pi=3.1415926; //錯誤 trait中不能有常量 }
3.trait是用來實現代碼的復用的,不可以被實例化也不可以被繼承
<?php // 定義trait trait show{ public $name; protected $host; //允許定義,但實際不用 private $info; public function show_time(){ echo $this->info; } protected function showhost(){ //允許定義,但實際不用 echo $this->name; } } new show(); //trait 不允許被實例化
4.trait是用來將公共代碼提供給其他類使用的,而類要使用trait的掐你是加載對應的trait
<?php // 定義trait trait Show{ public $name="d"; public function show_time(){ echo $this->name; } } //類中加載trait class add{ //加載:使用use關鍵字 use Show; //use 就表示將trait show中所有的東西拿到了當前類add中 } //使用trait中的內容 $s=new add(); $s->show_time(); //show add類中自己沒有show_time方法,但是因為使用了trait show 所以可用
5.一個類可以使用多個trait
<?php // 定義trait trait Show{ public $name="d"; public function show_time(){ echo $this->name; } } trait get{ public $age=18; public function get_age(){ echo $this->age; } } //類中加載trait class add{ //加載:使用use關鍵字 use Show; //use 就表示將trait show中所有的東西拿到了當前類add中 use get; } //使用trait中的內容 $s=new add(); $s->show_time(); //show add類中自己沒有show_time方法,但是因為使用了trait show 所以可用 $s->get_age();
6.如果同時引入的多個trait中有同名方法,那么會產生沖突:解決沖突的方法是使用insteadof代替處理以及對被代替的方法使用別名
<?php // 定義trait trait show{ public $name="d"; public function show_time(){ echo $this->name; } } trait get{ public $age=18; public function show_time(){ echo $this->age; } } //類中加載trait class add{ // use get,show; //錯誤 2個trait存在同名的方法 // 方案1 使用其中一個同名方法 use get,show{ // 意思 使用show中的show_time 代替get中的show_time,並且會被覆蓋無法使用 show::show_time insteadOf get; // 方案2 給get中的show_time取一個別名 就可以使用了。 get::show_time as show_insteadostime; } } $s=new add(); $s->show_time(); //輸出的是d 而不是18 代替成功 $s->show_insteadostime(); //顯示18
7.同名覆蓋問題:如果類中有與引入的trait同名成員,會有不同處理
屬性:不允許重名,即類中不允許定於與trait中同名的成員屬性(靜態屬性也一樣)
方法:類覆蓋trait
<?php // 定義trait trait show{ public $name="d"; public function show_time(){ echo $this->name; } } trait get{ public $age=18; public function show_time(){ echo $this->age; } } //類中加載trait class add{ // use get,show; //錯誤 2個trait存在同名的方法 // 方案1 使用其中一個同名方法 use get,show{ // 意思 使用show中的show_time 代替get中的show_time,並且會被覆蓋無法使用 show::show_time insteadOf get; // 方案2 給get中的show_time取一個別名 就可以使用了。 get::show_time as show_insteadostime; } } $s=new add(); $s->show_time(); //輸出的是d 而不是18 代替成功 $s->show_insteadostime(); //顯示18
8.繼承覆蓋問題:如果類中在使用trait的同時,也是繼承自父類,而trait中與父類中有同名方法,那么trait中將覆蓋父類同名方法,如果要訪問父類方法,
可以在trait同名方法中使用parent關鍵字訪問父類同名方法。
<?php // 代碼復用 trait posen{ public $name="杜偉"; public $age=31; public function show(){ // 如果想用父類中的方法 parent::show(); echo $this->name.$this->age; } } // 父類 class human{ public function show(){ echo 'human::show<hr>'; } } // 定義子類 class son extends human{ use posen; } // 實例化 $s=new son(); $s->show(); //顯示的是杜偉 31 trait中的方法會覆蓋繼承中父類的方法
9.另外,trait自己不能訪問,只是用來給其他類提供代碼復用的,因此允許類在使用trait時更高里面方法的訪問控制權。在as 之后,使用目標訪問修飾限定符。
<?php // 代碼復用 trait posen{ private function show(){ echo '你看到我了'; } } class son{ use posen{ //因為我只是引用了一個show,如果是兩個的話 需要具體的體現是哪個 posen::show(這個) show as public pshow; //注意 as是用來起別名的,雖然沒有同名show 但是系統認為已經存在,並且改變trait中show的訪問權限為public 變成了一個新的方法,原來的方法不動。 } } // 實例化 $s=new son(); $s->pshow(); //調用別名方法
10.trait中可以使用抽象方法,用來規范使用類必須實現對應抽象方法,使用了欸要么為抽象類,要么就必須實現抽象方法
<?php // 代碼復用 trait posen{ abstract public function add(); //trait 定義抽象方法 } // 第一種方式 abstract class man{ use posen; //抽象類可以不實現抽象方法 } // 第二種 class son{ use posen; public function add(){ echo '實現了trait中的抽象方法'; //實現抽象方法 } }
總結:
1.trait的hi一種類似class結構關鍵字,trait不能被實例化,可以擁有所有類成員結構
2.trait是用來實現代碼復用的,為其他類提供公共代碼(方法),其他類如果使用trait用use關鍵字引入
3.在了類中use是具體trait就相當於將trait內部的所有代碼在類中寫了一遍
4.一個類中可以有多個trait,但是要注意同名問題
同名方法可以使用insteadof來實現代替:一個trait中的同名方法代替另外一個,類就訪問替代的那個
同名方法可以在被替代之后使用as制作方法使用as制作方法別名:類就可以擁有兩個方法
5.類中在引入trait后,要注意與trait中的同名成員問題
同名屬性:不允許
同名方法:允許,類中的方法會覆蓋trait中的方法
6.如果在使用trait的同時也繼承其他類,那么trait中出現的同名方法會覆蓋基類的同名方法
7.類在使用trait時可以修改trait方法的控制級別,更嚴或這更寬松都可以,注意修改控制級別時使用的時別名機制,一定要改成別名:trait名::方法名 as 訪問修飾限定符 別名;
8.trait中可以使用抽象方法,那么使用該trait的類就必須本身為抽象類或者將抽象方法實現
9.trait使用機制
有公共代碼要實現(方法),而這些方法可能在很多類中會用到
公共代碼不是屬於某一類事務特有,而是很多事務都有(不符合繼承)