Whip Monstrous Code Into Shape - 05 God Object Cleanup #2: Traits and Socks

file

Let's review the next option you have, when cleaning up big, bad God objects: extracting traits. While some developers have knee-jerk reactions to the concept of a trait that will never be used elsewhere, I find it to be a clean and convenient solution. It's akin to cleaning up your room, by placing all the socks in one drawer, and the shirts in another. Admittedly, you didn't design a new closet space, but there's no denying that the room is now cleaner as a result.

这节视频讲的使用 trait 来清理我们的模型。首先看看文档中对于 trait 的定义:Trait 是为类似 PHP 的单继承语言而准备的一种代码复用机制。Trait 为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用 method。Trait 和 Class 组合的语义定义了一种减少复杂性的方式,避免传统多继承和 Mixin 类相关典型问题。

可以看到官方对 trait 的定位也是为了解决单继承的限制,所以一般不会用来做其他的,并且有些开发者也很反对到处用 trait,相对来说会导致代码更难以理解。不过就像视频中 jeff 讲的例子,使用 trait 就像整理房间的时候,把所有的袜子放到一个抽屉,把所有的衬衫放到另外一个抽屉,虽然没有开发一个新的橱柜,但不可否认的是现在房间更干净了。

里面有个例子,比如在 User 模型里面有以下方法,可以看到这几个方法的功能方面都是有关联的,所以可以把这几个方法写在一个 Completable trait里面,然后在控制器中 use Completable 就行。

public function completions()
{
    // fetch completions relationship
}

public function complete()
{
    // reference relationship to complete item
}

public function uncompleted()
{
    // reference relationship to uncompleted item
}

public function completed(Video $video)
{
    // check if item is completed by user
}

在 laravel 里面也有类似的用法,比如下面的这三个在 User 模型中使用到的 trait,跟 Notifiable,SoftDeletes 这些 trait 不一样,除非你要自己写认证的用户模型,否则在其他地方不可能会用到,同样的 Laravel 也把这些方法放到 trait 中。

use Illuminate\Auth\Authenticatable;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Foundation\Auth\Access\Authorizable;

这样使用 trait 之后有两个好处,第一是 user 类看起来更像我们所说的 dramatic over,另外一方面,应该是方法的责任更明确吧,可以把责任相同的一些方法放在一起(我没太听懂。。。)。视频最后又说到,并没有一种 100% 合适的方法,你要做的就是适当的清理你的类,区分不同类的责任(responsibility),然后把相应责任的放到对应的类中,而不要一味的堆在一起。

最后,在模型中使用 trait 的时候,如果你的 trait 需要初始化一些数据,你可以在 trait 中添加 bootTraitName 静态方法,这个方法会在使用这个 trait 的模型第一次 boot 的时候调用,比如 "rtconner/laravel-tagging" 这个包,在 Taggable trait 里面就用了这个方法,在这里注册监听模型删除和保存事件的闭包。

public static function bootTaggable()
{
    if(static::untagOnDelete()) {
        static::deleting(function($model) {
            $model->untag();
        });
    }

    static::saved(function ($model) {
        $model->autoTagPostSave();
    });

    static::$taggingUtility = app(TaggingUtility::class);
}

PS: Summer哥 & JobsLong哥, 不要加精了吧,我觉得写的很烂,没到加精的水平,主要是想跟大家交流一下,还没到发这些技术文章的水平,不要拉低咱社区的水准啊。:smile:

本帖已被设为精华帖!
本帖由 Summer 于 7年前 加精
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 3
Summer

感谢分享,大鲲

7年前 评论
superwen

挺好的,一个细节的功能讲这么细。学习了。

7年前 评论

@Summer 龙哥谬赞了,今天一天都在听我们老板一个“宏伟”的项目,用 laravel 开发,听晕了 :astonished:

7年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!