php trait 详解+示例

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的具体用法啦

  • 声明的关键字: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、基类和本类中都存在某个同名的属性或者方法,会如何处理呢,我们接着看代码示例


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;
  • 多个特性冲突
    哈哈,你可能在想,一个类有多个特性时,,假设 superman 包含 myTrait外,还包含myTrait1,这两个特性都有see方法,执行之后,会怎么样呢,那当然会报错了,而且是一个致命的错误,这时候需要申明解决冲突了,继续看示例


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

  • 修改访问权限
    其实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里面组合trait,并支持抽象方法,静态属性,静态方法
    接下来,在看一组不太常用的用法,trait组合trait,并且trait中支持抽象方法,静态属性,以及静态方法,so,你把他看成一种有个性的类,他有这些东东也就不奇怪了,最后在看一个示例


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呢。

你可能感兴趣的:(php)