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

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

1. 继承 VS 多态 VS Trait

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

  1. 继承
  2. 多态
  3. Trait

1.1. 继承

如图:

代码结构如下:

// Log.php
<?php
Class Log
{public function startLog(){// echo ...}public function endLog(){// echo ...}
}
// Publish.php
<?php
Class Publish extends Log
{}
// Answer.php
<?php
Class Answer extends Log
{}

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

1.2. 多态

如图:

实现代码:

// Log.php
<?php
Interface Log
{public function startLog();public function endLog();
}
// Publish.php
<?php
Class Publish implements Log
{public function startLog(){// TODO: Implement startLog() method.}public function endLog(){// TODO: Implement endLog() method.}
}
// Answer.php
<?php
Class 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

如图:

实现代码如下:

// Log.php
<?php
trait Log{public function startLog() {// echo ..}public function endLog() {// echo ..}
}
// Publish.php
<?php
class Publish {use Log;
}
$publish = new Publish();
$publish->startLog();
$publish->endLog();
// Answer.php
<?php
class Answer {use Log;
}
$answer = new Answer();
$answer->startLog();
$answer->endLog();

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

1.4. 结论

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

2. Trait的作用域

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

<?php
class Publish {use Log;public function doPublish() {$this->publicF();$this->protectF();$this->privateF();}
}
$publish  = new Publish();
$publish->doPublish();

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

public function
protected function
private function

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

3. Trait中属性的优先级

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

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

<?php
trait 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,如下:

<?php
trait 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中如果出现了同名的属性或者方法该怎么办呢?这个时候就需要用到Insteadofas 这两个关键字了.请看如下实现代码:

<?phptrait 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内部使用的。

  • 其他介绍的Trait的文章:如何使用Trait?
  • 文章首发在:PHP中Trait详解及其应用

PHP中Trait详解及其应用相关推荐

  1. php动态+trait,详解PHP神奇又有用的Trait

    php和java,c++一样都是单继承模式.但是像python,是支持多继承(即Mixin模式).那么如何在php中实现多继承模式?这就需要使用trait. trait Arrayabletrait{ ...

  2. 函数中{}输出格式详解(C#)

    Console.WriteLine()函数中{}输出格式详解(C#) Console.WriteLine()函数的格式一直没怎么注意.今天同事问起Console.WriteLine({0:D3},a) ...

  3. Java中CAS详解

    转载自  Java中CAS详解 在JDK 5之前Java语言是靠synchronized关键字保证同步的,这会导致有锁 锁机制存在以下问题: (1)在多线程竞争下,加锁.释放锁会导致比较多的上下文切换 ...

  4. 【转】图形流水线中坐标变换详解:模型矩阵、视角矩阵、投影矩阵

    转自:图形流水线中坐标变换详解:模型矩阵.视角矩阵.投影矩阵_sherlockreal的博客-CSDN博客_视角矩阵 图形流水线中坐标变换详解:模型矩阵.视角矩阵.投影矩阵 图形流水线中坐标变换过程 ...

  5. oracle itl解析,oracle数据块dump文件中ITL详解

    oracle数据块dump文件中ITL详解 dump出Oracle block后,可以看到事物槽,包含有事物槽号(ITL),XID,UBA,FLAG,LCK,SCN. 本文主要讨论FLAG标记的规则, ...

  6. android中getSystemService详解

    原文地址:android中getSystemService详解作者:邹斌 http://blog.sina.com.cn/s/blog_71d1e4fc0100o8qr.html http://blo ...

  7. Oracle中CONCAT详解

    Oracle中CONCAT详解 1.什么是CONCAT 新的改变 我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写 ...

  8. Java中LinkedList详解

    Java中LinkedList详解 LinkedList底层是双向链表 单向链表 双向链表 LinkedList新增的方法 主要增加了针对头结点与尾结点进行操作的方法, 即针对第一个元素和最后一个元素 ...

  9. vue 生命周期的11中方法详解

    vue 生命周期的十一中方法详解 vue 生命周期的定义 vue实例 从创建到销毁,也就是说从创建 -> 初始化 数据-> 编译模版 -> 挂载Dom -> 渲染 更新 -&g ...

  10. oracle语句中dual什么意思,oracle中dual详解

    oracle中dual详解 基本上oracle引入dual为的就是符合语法 1. 我们先从名称来说,dual不是缩写词,本身就是完整的单词.dual名词意思是对数,做形容词时是指二重的,二元的. 2. ...

最新文章

  1. 软件体系架构模式之一什么是软件架构模式
  2. Java实现均摊_Java均摊复杂度和防止复杂度的震荡原理分析
  3. 【bzoj4832】[Lydsy1704月赛]抵制克苏恩 期望dp
  4. 0.11内核rd_load@ramdisk.c中memcpy函数好像有bug
  5. Python补充01 序列的方法
  6. Nginx教程系列二:Linux安装nginx
  7. Golang slice和map的申明和初始化
  8. Gemini论文笔记
  9. 最新Kernel 2.6.29正式版发布啦
  10. Google Maps 补丁绕过、得双倍奖金这么简单?我陷入了自我怀疑
  11. python 新建一列_python – 如何在迭代pandas数据框时创建新列并插入行值
  12. python起简易http server
  13. FTT字体绘制,2D阶段
  14. PHP - Redis实现入队出队
  15. html5教学案例撰写,教学案例撰写基本格式
  16. python滑稽脸代码_如何用python进行简单的syn flood滑稽(带代码@)
  17. 加密邮箱的数字签名和加密原理
  18. linux i2c dev.h freq,STM32F103ZET(基于秉火开发板)+Cubemx(F1 V1.60库)+IIC+AT24C02(修复官方例程读写死机bug)...
  19. 天不怕,地不怕,就怕李佳琦的“OMG,太好看了吧!”
  20. 【嵌入式】嵌入式开发为什么要跑操作系统?

热门文章

  1. Atitit Server Side Include  ssi服务端包含规范 csi  esi
  2. Atitit.加密算法ati Aes的框架设计
  3. atitit.RandomAccessFile rws rwd 的区别于联系
  4. Atitit.手机验证码的破解---伪随机数
  5. paip.提升安全性---登录密码出错次数检测
  6. PAIP.如何选择安全的即时通讯IM工具.txt
  7. 隐马尔科夫模型HMM学习最佳范例
  8. Linux内核开发者大会 开始报名啦~
  9. 毕设题目:Matlab智能算法VRP(车辆路径规划)
  10. 【优化算法】鲸鱼优化算法(WOA)【含Matlab源码 1243期】