Whip Monstrous Code Into Shape 14-Consider Fluent Interfaces
Fluent interfaces often get a bad rap, but the truth is that there are countless scenarios where you may consider leveraging them. In this episode, we'll review the basic definition and makeup for a fluent API, and will then move on discussing a handful of real-life examples in the wild.
这个视频讲的是使用 Fluent interface。Fluent interface 是一种 api 设计模式,简单的讲就是通过 return self
来达到“链式调用”的效果。这种方式最初的目的是为了"provide more readable code",写出可读性更好的代码,但是事实真的如此吗?在视频开头 jeff 找了一个反对者的博客,题目就是 “Fluent Interfaces are Evil”。Fluent interface 真的是 evil 吗?工具不是 evil,关键是如何使用。
在 Laravel
中很多地方都使用了 Fluent interface。比如 TestCase
、Query\Builder
、Pipeline
以及 laravel-elixir
。
TestCase:
public function testExample()
{
$this->get('/')
->see('Laravel');
}
Query\Builder:
$queryBuilder
->select('u')
->from('User u')
->where('u.id = :identifier')
->orderBy('u.name', 'ASC')
->setParameter('identifier', 100);
Pipeline:
$this->pipeline
->send($command)
->through($this->pipes)
->then($callback);
laravel-elixir:
elixir((mix) => {
mix.sass('app.scss')
.version('css/styles.css')
.webpack('app.js');
});
从这些调用来看,一定程度上使代码可读性变得更好,而且看起来和现实生活比较贴近。比如第一个例子,现实生活的逻辑就是访问一个路由,然后看到某个字符串,相比较下面这种调用更容易理解。
public function testExample()
{
$result = $this->call('get', '/');
$this->assertContains('Laravel', $result);
}
实现一个 Fluent interface 也很简单,每个方法调用完成后返回实例本身就行。
class AuthenticateUser
{
public function invite()
{
$this->validateRequest()
->createToken()
->send();
}
private function validateRequest()
{
// do something;
return $this;
}
private function createToken()
{
// do something;
return $this;
}
private function send()
{
// do something;
return $this;
}
}
这种写法也有一种弊端,就是没有办法控制调用的次序,比如下面这种调用方式肯定不会出错,但是逻辑上就有很大的问题:
$this->createToken()
->send()
->validateRequest();
解决办法是将不同种类的功能抽象到不同类中,方法内部不再使用return this,取而代之的是return 下一个处理类。
class AuthenticateUser
{
public function invite()
{
$this->validateRequest()
->createToken()
->send();
}
private function validateRequest()
{
// do something;
return new TokenCreator();
}
}
class TokenCreator
{
public function createToken()
{
//...
return new MailSender();
}
}
class MailSender
{
public function send()
{
return $this;
}
}
如果你想大量使用这种方法的话,务必要读一读这篇博客:“Fluent Interfaces are Evil”,在博客里面作者分析了很多弊端,比如:
- Fluent Interfaces break Encapsulation 破坏封装性;
- Fluent Interfaces break Decorators (and sometimes Composition) 破坏装饰器(基于作者自己的包,因为运用了大量的装饰器);
- Fluent Interfaces are harder to Mock 测试的时候很难 Mock;
- Fluent Interfaces make diffs harder to read 针对滥用的情况,可能使得代码阅读起来更加困难(里面有个极端例子,Fluent Interface 调用起来很爽,所以写了一个几十行的调用);
- Fluent Interfaces are less readable (personal feeling) 作者个人看法,觉得使得代码可读性更差;
- Fluent Interfaces cause BC breaks during early development stages 在早期开发阶段就会导致无法向后兼容;
每种方法可能有便利之处,也会有弊端,没必要一味的拒绝,也没有必要在每个项目中都把这些方法用一遍。把这些方法当作是一些 tools,在合适的时候使用合适的方法,说不定会让你事半功倍。
推荐文章: