Whip Monstrous Code Into Shape - 05 God Object Cleanup #2: Traits and Socks
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:
推荐文章: