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不能直接实例化
<?php

trait myTrait {

    public $myName = 'sunny trait';

    public function fly() {
        echo "I can fly<br />";
    }

}

class person {

    public function say() {
        echo "I can say <br />";
    }

}

class superman extends person {

    use myTrait;

    public function work() {
        echo "I am work<br />";
    }

}

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

trait myTrait {

    public $myName = 'sunny trait';

    public function fly() {
        echo "I can fly<br />";
    }

    public function see() {
        echo "myTrait can see <br />";
    }

}

class person {

    public function say() {
        echo "I can say <br />";
    }

    public function see() {
        echo "person can see <br />";
    }

}

class superman extends person {

    use myTrait;

    public function work() {
        echo "I am work<br />";
    }

    public function see() {
        echo "superman can see <br />";
    }

}

$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方法,执行之后,会怎么样呢,那当然会报错了,而且是一个致命的错误,这时候需要申明解决冲突了,继续看示例
<?php

trait myTrait {

    public $myName = 'sunny trait';

    public function fly() {
        echo "I can fly<br />";
    }

    public function see() {
        echo "myTrait can see <br />";
    }

}

trait myTrait1 {

    public $myName1 = 'sunny trait1';

    public function fire() {
        echo "I can fire<br />";
    }

    public function see() {
        echo "myTrait1 can see <br />";
    }

}

class person {

    public function say() {
        echo "I can say <br />";
    }

    public function see() {
        echo "person can see <br />";
    }

}

class superman extends person {

    use myTrait,
        myTrait1 {
        myTrait1::see insteadof myTrait;
    }

    public function work() {
        echo "I am work<br />";
    }

}

class superman1 extends person {

    use myTrait,
        myTrait1 {
        myTrait1::see insteadof myTrait;
        myTrait::see as seeAlias;
    }
    public function work() {
        echo "I am work<br />";
    }

}

$superMan = new superman();
$superMan->work();
$superMan->say();
$superMan->fly();
$superMan->see();
echo "<hr />";
$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 还可以支持修改方法的访问控制,这里不太赞同这种用法,破坏了封装性,继续看示例代码
<?php

trait myTrait {

    public $myName = 'sunny trait';

    public function fly() {
        echo "I can fly<br />";
    }

    public function see() {
        echo "myTrait can see <br />";
    }

}

class person {

    public function say() {
        echo "I can say <br />";
    }

    public function see() {
        echo "person can see <br />";
    }

}

class superman extends person {

    use myTrait {
        fly as protected;
        see as private seeAlias;
    }

    public function work() {
        echo "I am work<br />";
    }

}

$superMan = new superman();
$superMan->see(); //原来的see 方法时公共的所以可以正常输出
$superMan->seeAlias(); //必须报错,该别名的方法为私有的
$superMan->fly(); //报错,fly已经变更为保护类的了
  • trait里面组合trait,并支持抽象方法,静态属性,静态方法
    接下来,在看一组不太常用的用法,trait组合trait,并且trait中支持抽象方法,静态属性,以及静态方法,so,你把他看成一种有个性的类,他有这些东东也就不奇怪了,最后在看一个示例
<?php

trait myTrait {

    public $myName = 'sunny trait';

    public function fly() {
        static $fly = 0;
        $fly++;
        echo "I can fly,fly times $fly <br />";
    }

    public function see() {
        echo "myTrait can see <br />";
    }

}

trait myLastTrait {

    use myTrait;

    static public function jump() {
        echo "I can jump<br />";
    }

    abstract public function fire();
}

class person {

    public function say() {
        echo "I can say <br />";
    }

    public function see() {
        echo "person can see <br />";
    }

}

class superman extends person {

    use myLastTrait ;
    public function fire() {
        echo "I can fire <br />";
    }
    public function work() {
        echo "I am work<br />";
    }

}

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值