这可能是大家最疑问的地方,到底有和不同。
进入我们自己的解释之前,我先把yii2官方的说法粘贴一份过来,这是一个我们在选择用行为还是trait的一个标准。
官方的说明
尽管行为在 "注入" 属性和方法方面类似于 trait ,它们在很多方面却不相同。如上所述,它们各有利弊。它们更像是互补的而不是相互替代。
行为的优势
行为类像普通类支持继承。另一方面,trait 可以视为 PHP 语言支持的复制粘贴功能,它不支持继承。
行为无须修改组件类就可动态附加到组件或移除。要使用 trait,必须修改使用它的类。
行为是可配置的而 trait 不能。
行为以响应事件来自定义组件的代码执行。
当不同行为附加到同一组件产生命名冲突时,这个冲突通过先附加行为的优先权自动解决。而由不同 trait 引发的命名冲突需要通过手工重命名冲突属性或方法来解决。
trait 的优势
traits 比起行为更高效,因为行为是对象,消耗时间和内存。
IDE 对 trait 更友好,因为它们是语言结构。
行为只能服务于组件类,而 trait 没有这个限制。
总结一下
通过上面的说明,总结来说就一条:用行为、用行为、用行为。
-------------------我是分割线------------------------
虽然我们已经会了行为,官方也说你就用行为就行了。但是我们还是有必要了解下traits,它到底是什么?
traits的注入
首先要说的是traits的目的是解决类只能单继承的问题,行为也是这个目的,我们在第一篇前导课 - 什么是行为?就已经分析了 。
有一点很重要,就是trait不能被实例化。
我们来举一个 trait 的例子,让你知道什么是trait
trait Mouse {
public $name = '鼠标';
public function click() {
echo "鼠标点击了一下";
}
}
class Computer {
public function sayName(){
echo "我是一台电脑";
}
}
class Macbook extends Computer {
use Mouse;
public function say(){
echo "我是一条有逼格的macbook";
}
}
我来解释一下上面的代码,Macbook 和 Computer 是继承关系,Mouse 是一个trait ,Macbook 使用 use 关键字将 Mouse 注入到自己体内,此刻 Macbook 的 use 有点像yii2的 behaviors 函数。
接下来看看使用情况
$model= new Macbook ();
$model->say();// 我是一条有逼格的macbook
$model->sayName(); // 我是一台电脑
$model->click(); // 鼠标点击了一下
很高兴的是 trait 也同样注入了 Macbook 类增强了其功能,但是和行为不同是我们必须通过修改 Macbook 类代码实现,而yii2的行为可以通过动态绑定来实现对 Macbook 类的零修改。关于动态绑定可以参考之前干货文章 传送门
优先级
这个问题在行为和 trait 中都会存在,当使用它们的类、继承的类中具有相同成员时谁会被使用?
对于trait,优先顺序是来自当前类的成员覆盖了 trait 的方法,而 trait 则覆盖了被继承的方法。
对于行为,优先顺序是来自当前类的成员覆盖了被继承的方法,而继承的方法则覆盖了行为的方法。
没明白么?那看我的这个图。
在顺序上略有不同。
多个组合成员名称冲突问题
什么意思那,就是一个类同时使用了多个 trait 的情况,看下面代码。
trait Mouse {
public function click() {
echo "鼠标点击了一下";
}
}
trait Keyboard{
public function click() {
echo "键盘点击了一下";
}
}
class Macbook {
use Mouse,Keyboard;
}
$model = new Macbook();
$model->click();
我们看看结果
很不幸,对于此种情景PHP并没有给出自动处理方案,直接抛出了致命error。
这个时候我们必须人工参与,使用下面的关键词
insteadof
as
我们来说明下,还是上面的例子
...
class Macbook {
use Mouse,Keyboard {
Mouse::click insteadof Keyboard;
}
}
$model = new Macbook();
$model->click();
结果输出了 “鼠标点击了一下”,因为我们use的时候告诉php Mouse::click insteadof Keyboard,当冲突的时候使用Mouse的click代替Keyboard的同名方法。
当然我们还可以使用别名as,注意as无法替代insteadof,但是它可以让被替代者以其他的名字运行。
...
class Macbook {
use Mouse,Keyboard {
Mouse::click insteadof Keyboard;
Keyboard::click as keyClick;
}
}
$model = new Macbook();
$model->click();
$model->keyClick();
看看结果
我们解决了冲突。
yii2的多行为成员冲突问题
而对于yii2而言则按照先附加行为的优先权自动解决,并不会产生错误,具体如何解决本篇不再重复,可以去看下 揭秘yii2中行为的方法是如何注入到组件类中去的~ 这篇。
当然这种冲突解决方法会将后面的同名函数雪藏,而 trait 则可以将其通过as关键词再次启动起来。但是这个问题并不大,此种情景我们可以有太多方法解决了。
关于事件
event是个不能忽视的存在,通过前几篇我们都知道行为+事件可以拥有很多炫酷的注入能力,那么trait ?
当然也可以,但是会麻烦很多,我来举个例子。
// @app\components\EventTrait.php
namespace app\components;
use app\models\User;
trait EventTrait {
public function initEvent(){
User::on(User::EVENT_LOOK_ME,function(){
echo "i am event";
});
}
}
// @app\models\User.php
class User extends \yii\db\ActiveRecord {
...
const EVENT_LOOK_ME = 'event_look_me';
use EventTrait;
...
}
// 在action中
$model = new User();
$model->initEvent();
$model->trigger(User::EVENT_LOOK_ME);
虽然也可以实现,但是自然没有行为来得容易,对代码的侵入性也太强,不建议这样用。
其他
当然根据官方还有很多,比如行为可配置等,这些因为前几篇都已经进行了讲解,这里不再重复。
整体来说,yii2行为和trait 都可以解决单继承问题,并且也具有很不错的注入和组合能力,但是在各方面行为和yii2其他模块整合更好一点,因此推荐用yii2的行为。
不过这不是说你可以放弃 trait 了,记住,行为只能服务于组件类,但是yii2的世界里还有不是组件类的孩子们,请思考。
最后说一句,今天七夕,祝兄弟连所有有媳妇的、有媳妇的节日快乐,没媳妇的赶紧code。