4

这可能是大家最疑问的地方,到底有和不同。

进入我们自己的解释之前,我先把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 则覆盖了被继承的方法。

  • 对于行为,优先顺序是来自当前类的成员覆盖了被继承的方法,而继承的方法则覆盖了行为的方法。

没明白么?那看我的这个图。

alt

在顺序上略有不同。

多个组合成员名称冲突问题

什么意思那,就是一个类同时使用了多个 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();

看看结果

alt

我们解决了冲突。

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。



阿北
4.1k 声望913 粉丝

引用和评论

0 条评论