PHP编程中Trait详解及其应用
小职 2017-11-23 来源 :网络 阅读 1167 评论 0

摘要:从PHP的5.4.0版本开始,PHP提供了一种全新的代码复用的概念,那就是Trait。Trait其字面意思是”特性”、”特点”,我们可以理解为,使用Trait关键字,可以为PHP编程中的类添加新的特性。

从PHP的5.4.0版本开始,PHP提供了一种全新的代码复用的概念,那就是Trait。Trait其字面意思是”特性”、”特点”,我们可以理解为,使用Trait关键字,可以为PHP编程中的类添加新的特性。

熟悉面向对象的都知道,软件开发中常用的代码复用有继承和多态两种方式。在PHP中,只能实现单继承。而Trait则避免了这点。下面通过简单的额例子来进行对比说明。

1. 继承 VS 多态 VS Trait

现在有Publish.php和Answer.php这两个类。要在其中添加LOG功能,记录类内部的动作。有以下几种方案:

· 继承

· 多态

· Trait

1.1. 继承

如图:

 PHP编程中Trait详解及其应用

代码结构如下:

// Log.php<?phpClass Log{

    public function startLog()

    {

        // echo ...

    }

 

    public function endLog()

    {

        // echo ...

    }

}

// Publish.php<?phpClass Publish extends Log{

 

}

// Answer.php<?phpClass Answer extends Log{

 

}

可以看到继承的确满足了要求。但这却违背了面向对象的原则。而发布(Publish)和回答(Answer)这样的操作和日志(Log)之间的关系并不是子类与父类的关系。所以不推荐这样使用。

1.2. 多态

如图:

 PHP编程中Trait详解及其应用

实现代码:

// Log.php<?phpInterface Log{

    public function startLog();

    public function endLog();

}

// Publish.php<?phpClass Publish implements Log{

    public function startLog()

    {

        // TODO: Implement startLog() method.

    }

    public function endLog()

    {

        // TODO: Implement endLog() method.

    }

}

// Answer.php<?phpClass Answer implements Log{

    public function startLog()

    {

        // TODO: Implement startLog() method.

    }

    public function endLog()

    {

        // TODO: Implement endLog() method.

    }

}

记录日志的操作应该都是一样的,因此,发布(Publish)和回答(Answer)动作中的日志记录实现也是一样的。很明显,这违背了DRY(Don’t Repeat Yourself)原则。所以是不推荐这样实现的。

1.3. Trait

如图:

 PHP编程中Trait详解及其应用

实现代码如下:

// Log.php<?phptrait Log{

    public function startLog() {

        // echo ..

    }

    public function endLog() {

        // echo ..

    }

}

// Publish.php<?phpclass Publish {

    use Log;

}

$publish = new Publish();

$publish->startLog();

$publish->endLog();

// Answer.php<?phpclass Answer {

    use Log;

}

$answer = new Answer();

$answer->startLog();

$answer->endLog();

可以看到,我们在没有增加代码复杂的情况下,实现了代码的复用。

1.4. 结论

继承的方式虽然也能解决问题,但其思路违背了面向对象的原则,显得很粗暴;多态方式也可行,但不符合软件开发中的DRY原则,增加了维护成本。而Trait方式则避免了上述的不足之处,相对优雅的实现了代码的复用。

2. Trait的作用域

了解了Trait的好处,我们还需要了解其实现中的规则,先来说一下作用域。这个比较好证明,实现代码如下:

<?phpclass Publish {

    use Log;

    public function doPublish() {

        $this->publicF();

        $this->protectF();

        $this->privateF();

    }

}

$publish  = new Publish();

$publish->doPublish();

执行上述代码输出结果如下:

public functionprotected functionprivate function

可以发现,Trait的作用域在引用该Trait类的内部是都可见的。可以理解为use关键字将Trait的实现代码Copy了一份到引用该Trait的类中。

3. Trait中属性的优先级

说到优先级,就必须要有一个对比的参照物,这里的参照对象时引用Trait的类及其父类。

通过以下的代码来证明Trait应用中的属性的优先级:

<?phptrait Log

{

    public function publicF()

    {

        echo __METHOD__ . ' public function' . PHP_EOL;

    }

    protected function protectF()

    {

        echo __METHOD__ . ' protected function' . PHP_EOL;

    }

}

class Question{

    public function publicF()

    {

        echo __METHOD__ . ' public function' . PHP_EOL;

    }

    protected function protectF()

    {

        echo __METHOD__ . ' protected function' . PHP_EOL;

    }

}

class Publish extends Question{

    use Log;

 

    public function publicF()

    {

        echo __METHOD__ . ' public function' . PHP_EOL;

    }

    public function doPublish()

    {

        $this->publicF();

        $this->protectF();

    }

}

$publish = new Publish();

$publish->doPublish();

上述代码的输出结果如下:

Publish::publicF public function

Log::protectF protected function

通过上面的例子,可以总结出Trait应用中的优先级如下:

1. 来自当前类的成员覆盖了 trait 的方法

2. trait 覆盖了被继承的方法

类成员优先级为:当前类>Trait>父类

4. Insteadof和As关键字

在一个类中,可以引用多个Trait,如下:

<?phptrait Log

{

    public function startLog()

    {

        echo __METHOD__ . ' public function' . PHP_EOL;

    }

    protected function endLog()

    {

        echo __METHOD__ . ' protected function' . PHP_EOL;

    }

}

trait Check

{

    public function parameterCheck($parameters) {

        // do sth

    }

}

class Publish extends Question{

    use Log,Check;

    public function doPublish($para) {

        $this->startLog();

        $this->parameterCheck($para);

        $this->endLog();

    }

}

通过上面的方式,我们可以在一个类中引用多个Trait。引用多个Trait的时候,就容易出问题了,最常见的问题就是两个Trait中如果出现了同名的属性或者方法该怎么办呢?这个时候就需要用到Insteadof 和 as 这两个关键字了.请看如下实现代码:

<?php

trait Log

{

    public function parameterCheck($parameters)

    {

        echo __METHOD__ . ' parameter check' . $parameters . PHP_EOL;

    }

 

    public function startLog()

    {

        echo __METHOD__ . ' public function' . PHP_EOL;

    }

}

trait Check

{

    public function parameterCheck($parameters)

    {

        echo __METHOD__ . ' parameter check' . $parameters . PHP_EOL;

    }

 

    public function startLog()

    {

        echo __METHOD__ . ' public function' . PHP_EOL;

    }

}

class Publish{

    use Check, Log {

        Check::parameterCheck insteadof Log;

        Log::startLog insteadof Check;

        Check::startLog as csl;

    }

 

    public function doPublish()

    {

        $this->startLog();

        $this->parameterCheck('params');

        $this->csl();

    }

}

 

$publish = new Publish();

$publish->doPublish();

执行上述代码,输出结果如下:

Log::startLog public function

Check::parameterCheck parameter checkparams

Check::startLog public function

就如字面意思一般,insteadof关键字用前者取代了后者,as 关键字给被取代的方法起了一个别名。

在引用Trait时,使用了use关键字,use关键字也用来引用命名空间。两者的区别在于,引用Trait时是在class内部使用的。

 

希望这篇PHP编程文章可以帮助到你。总之,同学们,你想要的职坐标PHP频道都能找到!

本文由 @小职 发布于职坐标。未经许可,禁止转载。
喜欢 | 1 不喜欢 | 0
看完这篇文章有何感觉?已经有1人表态,100%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式AI+学习就业服务平台 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved