创buildstream畅的API

如何创build一个stream畅的API?

这是主要使用扩展方法吗?

这篇文章比我所能解释的要好得多。

编辑,不能在评论中挤压这个…

有两方面的接口,实现和使用。 在创作方面还有更多的工作要做,我同意,但是主要的好处可以在事物的使用方面find。 事实上,对我来说,stream畅的界面的主要优势是更自然,更容易记住和使用,为什么不,更美观的API。 也许,不得不以stream利的forms挤出一个API的努力可能导致更好地思考API?

正如Martin Fowler在关于stream畅接口的原始文章中所说:

关于这种风格可能最重要的一点是,其目的是按照内部的域特定语言来做一些事情。 确实,这就是为什么我们select“stream利”这个词来形容它,在很多方面这两个词都是同义词。 API主要是为了可读性和stream动性而devise的。 这种stream畅性的价格在思考和APIbuild设本身上是更加努力的。 构造函数,setter和添加方法的简单API更容易编写。 开发一个stream畅的API需要很好的思考。

就像在大多数情况下API创build一次并重复使用一样,额外的努力可能是值得的。

而详细? 如果它服务于一个程序的可读性,我都是为了冗长的。

MrBlah,

虽然可以编写扩展方法来编写stream畅的界面,但更好的方法是使用构build器模式。 我和你在同一条船上,我试图弄清楚stream畅接口的一些高级function。

下面你会看到我在另一个线程中创build的一些示例代码

public class Coffee { private bool _cream; private int _ounces; public static Coffee Make { get { return 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); 

虽然很多人把马丁·福勒(Martin Fowler)列为stream利的API讨论中的一位杰出代表,但他的早期devise主张实际上是围绕stream畅的构build模式或方法链而进化的。 stream利的API可以进一步演变成实际的内部领域特定的语言 。 一篇文章解释了一个文法的BNF符号如何被手动转换成“stream畅的API”,可以在这里看到:

http://blog.jooq.org/2012/01/05/the-java-fluent-api-designer-crash-course/

它转换这个语法:

在这里输入图像说明

进入这个Java API:

 // Initial interface, entry point of the DSL interface Start { End singleWord(); End parameterisedWord(String parameter); Intermediate1 word1(); Intermediate2 word2(); Intermediate3 word3(); } // Terminating interface, might also contain methods like execute(); interface End { void end(); } // Intermediate DSL "step" extending the interface that is returned // by optionalWord(), to make that method "optional" interface Intermediate1 extends End { End optionalWord(); } // Intermediate DSL "step" providing several choices (similar to Start) interface Intermediate2 { End wordChoiceA(); End wordChoiceB(); } // Intermediate interface returning itself on word3(), in order to allow // for repetitions. Repetitions can be ended any time because this // interface extends End interface Intermediate3 extends End { Intermediate3 word3(); } 

Java和C#有点类似,这个例子当然也转化为你的用例。 上面的技术已经在jOOQ中得到了大量的使用, 这是一种stream行的API /内部领域特定语言,在Java中对SQL语言进行build模

这是一个非常古老的问题,这个答案应该是一个评论,而不是一个答案,但我认为这是一个值得继续讨论的话题,而这个答复太长而无法评论。

关于“stream畅性”的原始思考似乎基本上是在增加对象的能力和灵活性(方法链接等)的同时使代码更加自我解释。

例如

 Company a = new Company("Calamaz Holding Corp"); Person p = new Person("Clapper", 113, 24, "Frank"); Company c = new Company(a, 'Floridex', p, 1973); 

比“stream利”less

 Company c = new Company().Set .Name("Floridex"); .Manager( new Person().Set.FirstName("Frank").LastName("Clapper").Awards(24) ) .YearFounded(1973) .ParentCompany( new Company().Set.Name("Calamaz Holding Corp") ) ; 

但是对于我来说,后者并没有比现在更强大,更灵活或更易于理解

 Company c = new Company(){ Name = "Floridex", Manager = new Person(){ FirstName="Frank", LastName="Clapper", Awards=24 }, YearFounded = 1973, ParentCompany = new Company(){ Name="Calamaz Holding Corp." } }; 

事实上,我会称之为最后一个版本比以前更容易创build,阅读和维护,而且我会说它在幕后还需要更less的行李。 对我来说哪个是重要的,至less有两个原因:

1 – 与创build和维护对象层(无论是谁)相关的成本与创build和维护使用它们的代码相关的成本一样真实,相关且重要。

2 – embedded在对象层中的代码膨胀会造成同样多的(如果不是更多)问题,因为代码中的代码膨胀会消耗这些对象。

使用最新的版本意味着只需添加一行非常简单的代码就可以向公司类添加(可能有用的)属性。

这并不是说我觉得没有方法链接的地方。 我真的很喜欢能够做的事情(在JavaScript中)

 var _this = this; Ajax.Call({ url: '/service/getproduct', parameters: {productId: productId}, ) .Done( function(product){ _this.showProduct(product); } ) .Fail( function(error){ _this.presentError(error); } ); 

..(在我想象中的假设情况下)完成和失败是对原始Ajax对象的补充,并且能够被添加而不改变任何原始的Ajax对象代码或任何现有的使用原始的Ajax对象,并且不会创build作为代码的一般组织的例外的一次性事物。

所以我已经明确地find了使对象的函数的一个子集返回“this”对象的价值。 事实上,无论什么时候我有一个函数会返回void,我认为它会返回这个。

但是我还没有真正发现将“stream畅的接口”(.eg“Set”)添加到一个对象上的重要价值,尽pipe从理论上讲似乎可能存在一种类似于名称空间的代码组织这样做,这可能是值得的。 (“设置”可能不是特别有价值,但如果它帮助组织事情并促进和最小化添加和变化的影响,“命令”,“查询”和“转移”可能不是特别有价值。)这种做法的潜在好处之一,取决于它是如何完成的,可能会改善编码人员对保护级别的关心和关注程度 – 缺less这一点肯定会造成巨大的悲伤。

亲爱的:保持简单愚蠢。

stream利的devise是关于整个API使用的一种美学devise原则。 你在API中使用的方法可以稍微改变,但通常保持一致性会更好。

即使你可能认为“每个人都可以使用这个API,因为它使用了所有不同types的方法”。 事实是,用户会开始觉得迷失了,因为你一直在改变API的结构/数据结构到一个新的devise原则或命名约定。

如果你希望改变一半到不同的devise原则,例如..从错误代码转换到exception处理,因为一些更高的指挥权。 这将是愚蠢的,通常会在尾部的痛苦。 留住课程并添加客户可以使用和销售的function比让他们重新编写并重新发现所有问题更好。

从上面可以看出,在编写stream利的API方面还有很多工作要做。 在开始写作之前,有心理和审美的select,甚至是符合客户需求并保持一致的感觉,需求和愿望是最难的。

什么是stream畅的API

维基百科在这里定义它们http://en.wikipedia.org/wiki/Fluent_interface

为什么不使用stream畅的界面

我build议不要实现一个传统的stream畅的接口,因为它增加了你需要写的代码量,使代码复杂化,只是增加了不必要的样板。

另一种select,什么都不做!

不要执行任何操作。 不要提供设置属性的“简单”构造函数,也不要提供一个聪明的界面来帮助你的客户。 允许客户设置属性,但他们通常会。 在.NET C#或VB中,这可以像使用对象初始化器一样简单。

 Car myCar = new Car { Name = "Chevrolet Corvette", Color = Color.Yellow }; 

所以你不需要在代码中创build任何聪明的接口,这是非常可读的。

如果您有非常复杂的必须设置的属性集合,或者按照某个顺序设置,则使用单独的configuration对象,并通过单独的属性将其传递给类。

 CarConfig conf = new CarConfig { Color = Color.Yellow, Fabric = Fabric.Leather }; Car myCar = new Car { Config = conf }; 

stream利的API:

 myCar.SetColor(Color.Blue).SetName("Aston Martin"); 

看看这个videohttp://www.viddler.com/explore/dcazzulino/videos/8/

写一个stream利的API很复杂,这就是为什么我写了Diezel ,这是一个Java的stream利API生成器。 它使用接口(或课程)生成API来:

  1. 控制呼叫stream程
  2. 捕获genericstypes(如guice)

它也生成实现。

这是一个maven插件。

不,是的。 基础是一个很好的界面或者你想要stream利的types的界面。 具有扩展方法的库可以扩展此行为并返回接口。 扩展方法为其他人提供了使用更多方法扩展stream畅API的可能性。

一个好的stream畅的devise可能会很困难,需要一个相当长的反复试验时间才能完全调整基本构件。 只是configuration或设置stream利的API并不难。

学习构buildstream畅的API可以通过查看现有的API来完成。 将FluentNHibernate与stream畅的.NET API或ICriteriastream畅接口进行比较。 许多configurationAPI也被devise得“stream利”。