traits 产生背景
PHP 中的traits,是php5.4版本加入的新特性,大家都知道,php类的继承是单继承(接口可以多继承),哈哈,java也是单继承 ,也就是说,一个php类不能同时继承多个类
什么是traits
traits 的官方解释:Trait 是从 PHP 5.4 加入的一种细粒度代码复用的语法 ,是为类似 PHP 的单继承语言而准备的一种代码复用机制。Trait 为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用 method。Trait 和 Class 组合的语义定义了一种减少复杂性的方式,避免传统多继承和 Mixin 类相关典型问题
tratis 和interface的区别
interface 只是一组申明,形成了一个契约或者规范,并不关心怎么实现,traits 则相反,它关心的是具体的实现
taits 直译过来是特性,特征 ,特点,那到底什么是特性呢
看我举几个例子,看呀,马路上有一只会飞的猪,有比如,某商场促销,买电脑赠送鼠标 即电脑是可以出售的,鼠标数是不可出售的。其中 会飞是一种特性 , 可卖性是一种特性
那为什么要提取这种特性呢
继续商场的例子 按照传统的面向对象设计,我们会设计一个产品类, 基于产品类 扩展出一个电脑类和鼠标类,那可卖的商品和不可卖的商品怎么处理呢,你可以在基于产品类 扩展出一个goods 类,这goods里面定义价格属性和方法,当你有多个特性需要区别对待时,你的子类会越来越长,树形结构也会越来越复杂,这是traits就可以轻松解决了, 你只需在电脑类中引用 可卖性这个特性 便可轻松解决
traits的优势是什么
traits不仅仅是可复用代码的集合,它描述了某个特性的属性和方法的集合。可以非常轻量的,以较小的代价随意组合,降低了耦合,可读性也非常好
经过上面一大段的铺垫,相信已经知道traits的威力和使用场景了吧,那么现在我们来一起看一看traits的具体用法啦
trait myTrait {
public $myName = 'sunny trait';
public function fly() {
echo "I can fly
";
}
}
class person {
public function say() {
echo "I can say
";
}
}
class superman extends person {
use myTrait;
public function work() {
echo "I am work
";
}
}
$superMan = new superman();
$superMan->work();
$superMan->say();
$superMan->fly();
输出:
I am work
I can say
I can fly
Tips:为什么有个类取名superman,因为他比其他类多了一个特性 myTrait,这个特性是可以fly,所以会飞的人,我们称为superman。即 superman=person+myTrait.
trait myTrait {
public $myName = 'sunny trait';
public function fly() {
echo "I can fly
";
}
public function see() {
echo "myTrait can see
";
}
}
class person {
public function say() {
echo "I can say
";
}
public function see() {
echo "person can see
";
}
}
class superman extends person {
use myTrait;
public function work() {
echo "I am work
";
}
public function see() {
echo "superman can see
";
}
}
$superMan = new superman();
$superMan->work();
$superMan->say();
$superMan->fly();
$superMan->see();
输出
I am work
I can say
I can fly
superman can see
Tips:当traits和使用该特性类及该类的父类 的属性或方法重名时,优先级当前类>traits>父类(superman>myTrait>person)
use trait1,trait2;
trait myTrait {
public $myName = 'sunny trait';
public function fly() {
echo "I can fly
";
}
public function see() {
echo "myTrait can see
";
}
}
trait myTrait1 {
public $myName1 = 'sunny trait1';
public function fire() {
echo "I can fire
";
}
public function see() {
echo "myTrait1 can see
";
}
}
class person {
public function say() {
echo "I can say
";
}
public function see() {
echo "person can see
";
}
}
class superman extends person {
use myTrait,
myTrait1 {
myTrait1::see insteadof myTrait;
}
public function work() {
echo "I am work
";
}
}
class superman1 extends person {
use myTrait,
myTrait1 {
myTrait1::see insteadof myTrait;
myTrait::see as seeAlias;
}
public function work() {
echo "I am work
";
}
}
$superMan = new superman();
$superMan->work();
$superMan->say();
$superMan->fly();
$superMan->see();
echo "
";
$superman1=new superman1();
$superman1->work();
$superman1->say();
$superman1->fly();
$superman1->see();
$superman1->seeAlias();
输出
I am work
I can say
I can fly
myTrait1 can see
I am work
I can say
I can fly
myTrait1 can see
myTrait can see
tips:可以使用insteadof 或者as 来解决冲突,请在仔细看在示例代码的insteadof 和 as
trait myTrait {
public $myName = 'sunny trait';
public function fly() {
echo "I can fly
";
}
public function see() {
echo "myTrait can see
";
}
}
class person {
public function say() {
echo "I can say
";
}
public function see() {
echo "person can see
";
}
}
class superman extends person {
use myTrait {
fly as protected;
see as private seeAlias;
}
public function work() {
echo "I am work
";
}
}
$superMan = new superman();
$superMan->see(); //原来的see 方法时公共的所以可以正常输出
$superMan->seeAlias(); //必须报错,该别名的方法为私有的
$superMan->fly(); //报错,fly已经变更为保护类的了
trait myTrait {
public $myName = 'sunny trait';
public function fly() {
static $fly = 0;
$fly++;
echo "I can fly,fly times $fly
";
}
public function see() {
echo "myTrait can see
";
}
}
trait myLastTrait {
use myTrait;
static public function jump() {
echo "I can jump
";
}
abstract public function fire();
}
class person {
public function say() {
echo "I can say
";
}
public function see() {
echo "person can see
";
}
}
class superman extends person {
use myLastTrait ;
public function fire() {
echo "I can fire
";
}
public function work() {
echo "I am work
";
}
}
$superMan = new superman();
$superMan->fly();
$superMan->fly();
$superMan->see();
superman::jump();
输出
I can fly,fly times 1
I can fly,fly times 2
myTrait can see
I can jump
总结 在引用Trait时,使用了use关键字,use关键字也用来引用命名空间。两者的区别在于,引用Trait时是在class内部使用的,遇到方法和变量冲突时候,使用insteadof 或者as,编程的时候,可以将某一个特性的东西抽出来单独维护,以后有需要这个特性的时候,就可以直接use 进来,非常轻便。直接绕过了php单继承的缺点,同时也避免了继承的子代过深,有没有爱上trait呢。