让我的 class 'fluent'

Making my class 'fluent'

我昨天发现,如果我 return 来自每个方法的 class 实例,我可以 模拟 一个流畅的界面...

public class IsThisFluent
{
    public IsThisFluent Stuff()
    {
        //...
        return this;
    }

    public IsThisFluent OtherStuff()
    {
        // ...
        return this;
    }
}

仅此而已吗?

我承认,我是一只脑袋很小的熊,我想继续这个,但我认为最好还是和大人核实一下。

我是不是漏掉了什么?

有没有 'gotcha' 我没有发现这种模式?

不,差不多就是这样。

其背后的想法是,您可以将方法调用链接在一起,以随时操纵内部状态。 Fluent interface 的最终主要目标是可读性,LINQ 就是一个很好的例子。

差不多就这些了。

这是一篇关于它的非常好的文章: http://rrpblog.azurewebsites.net/?p=33

编辑
原来的网站似乎已经死了,所以这里是WayBackMachine to the rescue

我也非常喜欢这个答案中的这个例子:

public class Coffee
 {
    private bool _cream;

    public Coffee Make { get new Coffee(); }
    public Coffee WithCream()
    {
      _cream = true;
      return this;
    }
    public Coffee WithOuncesToServe(int ounces)
    {
      _ounces = ounces;
      return this;
    }
 }

var myMorningCoffee = Coffee.Make.WithCream().WithOuncesToServe(16);

这提醒了我,我现在需要一杯咖啡。

return this 不是 all 有流畅的界面。 链接方法是构建流式 API 的一种简单形式,但流式 API 通常看起来像 DSL(领域特定语言),并且 很多 更难实现设计。

以最小起订量为例:

new Mock<IInterface>()
    .Setup(x => x.Method())
    .CallBack<IInterface>(Console.WriteLine)
    .Returns(someValue);
  • Setup方法,定义在Mock<T>类型上,returns是ISetup<T, TResult>的一个实例。

  • Callback 方法,为 ICallback<TMock, TResult> returns IReturnsThrows<TMock,TResult> 的实例定义。请注意 ISetup<T, TResult> 扩展了 IReturnsThrows<TMock,TResult>.

  • 最后,Returns定义在IReturns<TMock,TResult>和returnsIReturnsResult<TMock>上。另请注意 IReturnsThrows<TMock,TResult> 扩展了 IReturnsResult<TMock>.

所有这些细微差别都是为了强制您以特定顺序调用这些方法,并禁止您连续调用 Setup 两次,例如。或者在调用 Setup.

之前调用 Returns

这些细节对于确保良好的用户体验非常重要。

要阅读有关设计流畅界面的更多信息,请查看 Martin Fowler 关于 FluentInterface. FluentAssertions 的文章,这是设计可能变得多么复杂的另一个主要示例 - 但结果的可读性也更高。