什么是 Trait
Trait?是從 PHP 5.4 加入的一種細(xì)粒度代碼復(fù)用的語法。以下是官方手冊對 Trait 的描述:
Trait 是為類似 PHP 的單繼承語言而準(zhǔn)備的一種代碼復(fù)用機(jī)制。Trait 為了減少單繼承語言的限制,使開發(fā)人員能夠自由地在不同層次結(jié)構(gòu)內(nèi)獨(dú)立的類中復(fù)用 method。Trait 和 Class 組合的語義定義了一種減少復(fù)雜性的方式,避免傳統(tǒng)多繼承和 Mixin 類相關(guān)典型問題。
Trait 和 Class 相似,但僅僅旨在用細(xì)粒度和一致的方式來組合功能。 無法通過 trait 自身來實(shí)例化。它為傳統(tǒng)繼承增加了水平特性的組合;也就是說,應(yīng)用的幾個(gè) Class 之間不需要繼承。
什么是 Trait ?
其實(shí)說通俗一點(diǎn),就是能把重復(fù)的方法拆分到一個(gè)文件,通過?use?引入以達(dá)到代碼復(fù)用的目的。
那么,我們應(yīng)該怎么樣去拆分我們的代碼才是合適的呢?我的看法是這樣的:
Trait,譯作?“特性”、“特征”、“特點(diǎn)”?。那么問題就來了:什么才是特性?
一個(gè)銷售公司有很多種產(chǎn)品:電視,電腦與鼠標(biāo)墊,卡通手辦等。其中鼠標(biāo)墊與卡通手辦是非賣品,只用于贈(zèng)送。
那么這里的 “可賣性” 就是一個(gè)特性,非賣品是沒有價(jià)格的。我們便可以抽象出 “可賣性” 這個(gè) Trait 來:
trait Sellable {protected $price = 0; public function getPrice() { return $this->price; } public function setPrice(int $price) { $this->price = $price; } }當(dāng)然我們所有的產(chǎn)品都會有品牌與其它基本屬性,所以我們通常會定義一個(gè)產(chǎn)品類:
class Pruduct {protected $brand; //... public function __construct($brand) { $this->brand = $brand; } public function getBrand() { return $this->brand; } //... }我們的電視與電腦類:
class TV extends Pruduct { use Sellable; //... public function play() { echo "一臺 {$this->brand} 電視在播放中..."; } //... } class Computer extends Pruduct { use Sellable; protected $cores = 8; //... public function getNumberOfCores() { return $this->cores; } //... }而鼠標(biāo)墊與手辦等禮品是不可賣的:
class Gift extends Pruduct { protected $name; function __construct($brand, $name) { parent::__construct($brand); $this->name = $name; } //... }上面的這個(gè)例子中,“可賣性” 便是部分商品的一個(gè)特性,也可以理解為商品的一個(gè)歸類。你也許會說,我也可以再添加一個(gè) Goods 類來完成上面的例子啊,Goods 繼承 Product,再讓所有可賣的商品繼承于 Goods 類,把價(jià)格屬性與方法寫到 Goods 里,同樣可以代碼復(fù)用啊。的確,這沒啥問題。但是你會發(fā)現(xiàn):你有多個(gè)需要區(qū)別的特性時(shí),由于 PHP 只有單繼承的原因,你不得不組合很多個(gè)基類出來,將他們層疊,最終得到的樹狀結(jié)構(gòu)是很復(fù)雜的。這也是 Trait 所帶來的優(yōu)勢:隨意組合,代碼清晰。
其實(shí)還有很多例子,比如可飛行的,那么把飛行這個(gè)特性所具有的屬性(如:高度,距離)與方法(如:起飛,降落)放到一個(gè) trait 就是一個(gè)合理的拆分。
Trait 有什么優(yōu)勢 ?
trait 有什么優(yōu)勢?來看一段代碼:
class User extends Model { use Authenticate, SoftDeletes, Arrayable, Cacheable; ... }這個(gè)用戶模型類,我們引入了四個(gè)特性:注冊與授權(quán)、軟刪除、數(shù)組式操作、可緩存。
我們看到代碼的時(shí)候一眼便知道當(dāng)前支持了哪些個(gè)特性。再看下面另外一種寫法:
abstract AdvansedUser {// ... 實(shí)現(xiàn)了 Authenticate, SoftDeletes, Arrayable, Cacheable 的所有方法 } class User extends AdvansedUser { ... }你不得不再去閱讀?AdvansedUser?的代碼才能理解。你想說沒有可讀性是因?yàn)槲一惖拿Q沒起好?可是,這種各種特性組合的一個(gè)基類是根本無法起一個(gè)見名知義的名稱的,不信你可以試一下。
就算你真的起了一個(gè)見名知義的名稱:AuthenticateCacheableAndArrayableSoftDeletesUser,可是當(dāng)需求變更,要求在?FooUser(同樣繼承了這個(gè)基類) 中去除緩存特性,而?User?類保留這個(gè)特性,怎么辦?再創(chuàng)建一個(gè)基類么?
這就是我理解的 Trait:
它不僅僅是可復(fù)用代碼段的集合,它應(yīng)該是一組描述了某個(gè)特性的的屬性與方法的集合。它的優(yōu)點(diǎn)在于隨意組合,耦合性低,可讀性高。
平常寫代碼的時(shí)候也許怎么拆分才是大家的痛點(diǎn),分享以下幾個(gè)技巧:
- 從需求或功能描述拆分,而不是寫了兩段代碼發(fā)現(xiàn)代碼一樣就提到一起;
- 拆分時(shí)某些屬性也一起帶走,比如上面第一個(gè)例子里的價(jià)格,它是“可賣性”必備的屬性;
- 拆分時(shí)如果給 Trait 起名困難時(shí),請認(rèn)真思考你是否真的拆分對了,因?yàn)檎_的拆分是很容易描述 “它是一個(gè)具有什么功能的特性” 的;
總之一定要記住:不要為了讓兩段相同的代碼提到一起這樣簡單粗暴的方式來拆分。
以上是個(gè)人見解,歡迎各位討論。??
?
from:http://overtrue.me/articles/2016/04/about-php-trait.html
轉(zhuǎn)載于:https://www.cnblogs.com/shynshyn/p/7928712.html
總結(jié)
- 上一篇: 前序中序确认二叉树 7-23 还原二
- 下一篇: [codeforces] 527A Pl