如何创建一个流畅的API?
这主要使用扩展方法吗?
这篇文章比我更好地解释了它.
编辑,不能在评论中挤压这个...
接口,实现和使用有两个方面.在创作方面还有更多的工作要做,我同意这一点,但是主要的好处可以在使用方面找到.事实上,对我来说,流畅的界面的主要优点是更自然,更容易记住和使用,为什么不,更美观的API.也许,不得不以流畅的形式挤压API的努力可能会导致更好的思考API?
正如Martin Fowler在关于流畅界面的原始文章中所说:
关于这种风格可能最重要的一点是,意图是按照内部DomainSpecificLanguage的方式做一些事情.实际上,这就是为什么我们选择"流利"一词来描述它,在很多方面这两个术语都是同义词.API主要设计为可读和流动.这种流畅性的代价在思考和API构建本身都是更多的努力.构造函数,setter和add方法的简单API更容易编写.提供一个漂亮的流畅API需要一点点思考.
在大多数情况下,API会被创建一次并一次又一次地使用,额外的努力可能是值得的.
而且冗长?如果它能提供程序的可读性,那么我的全部都是冗长的.
MrBlah,
虽然您可以编写扩展方法来编写流畅的接口,但更好的方法是使用构建器模式.我和你在同一条船上,我正在试图找出一些流畅接口的高级功能.
下面你会看到我在另一个线程中创建的一些示例代码
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在流畅的API讨论中是一个重要的指数,但他早期的设计主张实际上是围绕一个流利的建设者模式或方法链.流畅的API可以进一步演变为实际的内部域特定语言.可以在这里看到一篇文章,解释如何将语法的BNF符号手动转换为"流畅的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中大量使用,jOOQ是一种流畅的API /内部域特定语言,用Java模拟SQL语言
这是一个非常古老的问题,这个答案可能应该是一个评论而不是一个答案,但我认为这是一个值得继续讨论的话题,这个答案太长了,无法作出评论.
关于"流畅性"的原始思考似乎基本上是在为对象添加功能和灵活性(方法链接等),同时使代码更加不言自明.
例如
Company a = new Company("Calamaz Holding Corp"); Person p = new Person("Clapper", 113, 24, "Frank"); Company c = new Company(a, 'Floridex', p, 1973);
不那么"流利"
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." } };
..事实上,我会称之为最后一个版本比以前更容易创建,阅读和维护,我会说它在幕后需要的行李也少得多.对我来说很重要,因为(至少)有两个原因:
1 - 与创建和维护对象层相关的成本(无论是谁)与创建和维护使用它们的代码相关的成本一样真实,相关和重要.
2 - 嵌入在对象层中的代码膨胀会产生与代码消耗这些对象的代码膨胀一样多(如果不是更多)的问题.
使用最后一个版本意味着您只需添加一个非常简单的代码行即可向Company类添加(可能有用的)属性.
这并不是说我觉得没有方法链接的地方.我真的很喜欢能够做这样的事情(在JavaScript中)
var _this = this; Ajax.Call({ url: '/service/getproduct', parameters: {productId: productId}, ) .Done( function(product){ _this.showProduct(product); } ) .Fail( function(error){ _this.presentError(error); } );
..where(在我想象的假设情况下)Done和Fail是对原始Ajax对象的补充,并且能够在不改变任何原始Ajax对象代码或任何使用该对象的现有代码的情况下添加.原始的Ajax对象,而不创建一次性的东西,这是代码的一般组织的例外.
因此,我确实发现了使对象函数的子集返回'this'对象的价值.事实上,每当我有一个否则会返回void的函数时,我会考虑让它返回.
但是我还没有真正发现将"流畅的接口"(.eg"Set")添加到对象中的重要价值,尽管理论上似乎可能会出现一种类似命名空间的代码组织这样做,这可能是值得的.("设置"可能不是特别有价值,但"命令","查询"和"转移"可能,如果它有助于组织事物并促进和最小化添加和更改的影响.)这种做法的潜在好处之一,取决于它是如何完成的,可能是编码器典型的护理水平和对保护水平的关注 - 这种缺乏肯定会引起巨大的悲伤.
吻:保持简单愚蠢.
流畅的设计是关于整个API使用的一种美学设计原则.您在API中使用的方法可能略有变化,但通常最好保持一致.
即使您可能认为"每个人都可以使用此API,因为它使用了所有不同类型的方法".事实是,用户会开始感到迷茫,因为您始终将API的结构/数据结构更改为新的设计原则或命名约定.
如果你希望改变一半到不同的设计原则,例如..从错误代码转换为异常处理,因为一些更高的命令能力.这将是愚蠢的,并且通常会留下许多痛苦.最好保持课程并添加客户可以使用和销售的功能,而不是让他们重新编写并重新发现他们的所有问题.
根据上述内容,您可以看到编写Fluent API的工作量远远超出了人们的需求.在开始写一个之前,有一些心理和美学选择,即使这样,感觉,需要和愿望符合客户需求并保持一致是最难的.
什么是流畅的API
维基百科在这里定义了它们http://en.wikipedia.org/wiki/Fluent_interface
为什么不使用流畅的界面
我建议不要实现传统的流畅界面,因为它会增加您需要编写的代码量,使代码复杂化,只是添加了不必要的样板.
另一种选择,什么都不做!
不要实施任何东西.不要提供"简单"构造函数来设置属性,也不要提供巧妙的界面来帮助您的客户.允许客户端通常设置属性.在.Net C#或VB中,这可以像使用对象初始化器一样简单.
Car myCar = new Car { Name = "Chevrolet Corvette", Color = Color.Yellow };
因此,您不需要在代码中创建任何聪明的界面,这是非常易读的.
如果你有非常复杂的设置属性必须设置,或者按照一定的顺序设置,然后使用一个单独的配置对象,并通过一个单独的属性将它传递给类.
CarConfig conf = new CarConfig { Color = Color.Yellow, Fabric = Fabric.Leather }; Car myCar = new Car { Config = conf };