PHP中Trait详解及其应用
从PHP的5.4.0版本开始,PHP提供了一种全新的代码复用的概念,那就是Trait。Trait其字面意思是"特性"、"特点",我们可以理解为,使用Trait关键字,可以为PHP中的类添加新的特性。
熟悉面向对象的都知道,软件开发中常用的代码复用有继承和多态两种方式。在PHP中,只能实现单继承。而Trait则避免了这点。下面通过简单的额例子来进行对比说明。
1. 继承 VS 多态 VS Trait
现在有Publish.php
和Answer.php
这两个类。要在其中添加LOG功能,记录类内部的动作。有以下几种方案:
- 继承
- 多态
- 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应用中的优先级如下:
- 来自当前类的成员覆盖了 trait 的方法
- 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中如果出现了同名的属性或者方法该怎么办呢?这个时候就需要用到Insteadof
和 as
这两个关键字了.请看如下实现代码:
<?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详解及其应用相关推荐
- php动态+trait,详解PHP神奇又有用的Trait
php和java,c++一样都是单继承模式.但是像python,是支持多继承(即Mixin模式).那么如何在php中实现多继承模式?这就需要使用trait. trait Arrayabletrait{ ...
- 函数中{}输出格式详解(C#)
Console.WriteLine()函数中{}输出格式详解(C#) Console.WriteLine()函数的格式一直没怎么注意.今天同事问起Console.WriteLine({0:D3},a) ...
- Java中CAS详解
转载自 Java中CAS详解 在JDK 5之前Java语言是靠synchronized关键字保证同步的,这会导致有锁 锁机制存在以下问题: (1)在多线程竞争下,加锁.释放锁会导致比较多的上下文切换 ...
- 【转】图形流水线中坐标变换详解:模型矩阵、视角矩阵、投影矩阵
转自:图形流水线中坐标变换详解:模型矩阵.视角矩阵.投影矩阵_sherlockreal的博客-CSDN博客_视角矩阵 图形流水线中坐标变换详解:模型矩阵.视角矩阵.投影矩阵 图形流水线中坐标变换过程 ...
- oracle itl解析,oracle数据块dump文件中ITL详解
oracle数据块dump文件中ITL详解 dump出Oracle block后,可以看到事物槽,包含有事物槽号(ITL),XID,UBA,FLAG,LCK,SCN. 本文主要讨论FLAG标记的规则, ...
- android中getSystemService详解
原文地址:android中getSystemService详解作者:邹斌 http://blog.sina.com.cn/s/blog_71d1e4fc0100o8qr.html http://blo ...
- Oracle中CONCAT详解
Oracle中CONCAT详解 1.什么是CONCAT 新的改变 我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写 ...
- Java中LinkedList详解
Java中LinkedList详解 LinkedList底层是双向链表 单向链表 双向链表 LinkedList新增的方法 主要增加了针对头结点与尾结点进行操作的方法, 即针对第一个元素和最后一个元素 ...
- vue 生命周期的11中方法详解
vue 生命周期的十一中方法详解 vue 生命周期的定义 vue实例 从创建到销毁,也就是说从创建 -> 初始化 数据-> 编译模版 -> 挂载Dom -> 渲染 更新 -&g ...
- oracle语句中dual什么意思,oracle中dual详解
oracle中dual详解 基本上oracle引入dual为的就是符合语法 1. 我们先从名称来说,dual不是缩写词,本身就是完整的单词.dual名词意思是对数,做形容词时是指二重的,二元的. 2. ...
最新文章
- 软件体系架构模式之一什么是软件架构模式
- Java实现均摊_Java均摊复杂度和防止复杂度的震荡原理分析
- 【bzoj4832】[Lydsy1704月赛]抵制克苏恩 期望dp
- 0.11内核rd_load@ramdisk.c中memcpy函数好像有bug
- Python补充01 序列的方法
- Nginx教程系列二:Linux安装nginx
- Golang slice和map的申明和初始化
- Gemini论文笔记
- 最新Kernel 2.6.29正式版发布啦
- Google Maps 补丁绕过、得双倍奖金这么简单?我陷入了自我怀疑
- python 新建一列_python – 如何在迭代pandas数据框时创建新列并插入行值
- python起简易http server
- FTT字体绘制,2D阶段
- PHP - Redis实现入队出队
- html5教学案例撰写,教学案例撰写基本格式
- python滑稽脸代码_如何用python进行简单的syn flood滑稽(带代码@)
- 加密邮箱的数字签名和加密原理
- linux i2c dev.h freq,STM32F103ZET(基于秉火开发板)+Cubemx(F1 V1.60库)+IIC+AT24C02(修复官方例程读写死机bug)...
- 天不怕,地不怕,就怕李佳琦的“OMG,太好看了吧!”
- 【嵌入式】嵌入式开发为什么要跑操作系统?
热门文章
- Atitit Server Side Include ssi服务端包含规范 csi esi
- Atitit.加密算法ati Aes的框架设计
- atitit.RandomAccessFile rws rwd 的区别于联系
- Atitit.手机验证码的破解---伪随机数
- paip.提升安全性---登录密码出错次数检测
- PAIP.如何选择安全的即时通讯IM工具.txt
- 隐马尔科夫模型HMM学习最佳范例
- Linux内核开发者大会 开始报名啦~
- 毕设题目:Matlab智能算法VRP(车辆路径规划)
- 【优化算法】鲸鱼优化算法(WOA)【含Matlab源码 1243期】