最近遇到一个问题,我在 Trait 中写了个写了个 构造函数 然后妄图注入一个 Laravel服务 后面再使用这个 Trait 的时候发生服务的初始化报错(别问我为什么这么干,也是脑子瓦特了才能有这种骚操作…) 找了一圈发现是优先级的问题 顺带补了下 Trait 的使用
Trait 是什么
自 PHP 5.4.0 起,PHP 实现了一种代码复用的方法,称为 trait。
Trait 是为类似 PHP 的单继承语言而准备的一种代码复用机制。Trait 为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用 method。Trait 和 Class 组合的语义定义了一种减少复杂性的方式,避免传统多继承和 Mixin 类相关典型问题。
Trait 和 Class 相似,但仅仅旨在用细粒度和一致的方式来组合功能。 无法通过 trait 自身来实例化。它为传统继承增加了水平特性的组合;也就是说,应用的几个 Class 之间不需要继承。
Trait 翻译过来就是:特性的意思。 他提供了另一种维度的代码复用 我的理解就是你在引入的时候就等于帮他你把 Trait 中的 Function 复制到了你当前类中 可以让你直接调用
优先级问题
对于我上面犯的错就是典型的优先级问题 官方说的是 优先顺序是当前类中的方法会覆盖 trait 方法,而 trait 方法又覆盖了基类中的方法
首先我定义了一个 trait
|
|
然后在类中引入 trait
调用
|
|
这个时候会有一个报错 Call to a member function sayHello() on null
这里其实就是优先级的问题 从本质上 你引用trait 就是把他里面的方法复制到了你当前类中, 而我当前类中有一个空的构造函数 就算你在trait中定义了构造(除了我这种脑抽的人应该也没有人会在taint中在定义构造函数了…),根据优先级顺序是当前类覆盖trait,所以trait使用的是当前类的空的构造函数 并不会使用他本身的那个带注入 TestService
的构造函数 所以导致在 showHello
方法中 $this->service
就是一个 null
无法调用到最终的方法
多个Trait处理
在来看一种情况 如果你引入了2个Trait 而且其中都包含一个同名的方法 这个时候调用问题
|
|
这个时候如果你运行的话会有报错
|
|
解决冲突
使用 insteadof(代替)
|
|
使用 insteadof + as
|
|
关于代码拆分
其中看了 安正超 的文章后感觉学到了很多 引用下
Trait的优点在于随意组合,耦合性低,可读性高。
平常写代码的时候也许怎么拆分才是大家的痛点,分享以下几个技巧:
从需求或功能描述拆分,而不是写了两段代码发现代码一样就提到一起;
拆分时某些属性也一起带走,比如上面第一个例子里的价格,它是“可卖性”必备的属性;
拆分时如果给 Trait 起名困难时,请认真思考你是否真的拆分对了,因为正确的拆分是很容易描述 “它是一个具有什么功能的特性” 的;
参考
http://php.net/manual/zh/language.oop5.traits.php
https://overtrue.me/articles/2016/04/about-php-trait.html
https://zhuanlan.zhihu.com/p/31362082