C# 创建流畅的 API

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1622662/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 19:28:27  来源:igfitidea点击:

Creating API that is fluent

c#fluent

提问by mrblah

How does one go about create an API that is fluent in nature?

如何创建一个自然流畅的 API?

Is this using extension methods primarily?

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

采纳答案by fvu

This articleexplains it much better than I ever could.

这篇文章比我以往任何时候都更好地解释了它。

EDIT, can't squeeze this in a comment...

编辑,不能在评论中压缩这个...

there are two sides to interfaces, the implementation and the usage. There's more work to be done on the creation side, I agree with that , however the main benefits can be found on the usage side of things. Indeed, for me the main advantage of fluent interfaces is a more natural, easier to remember and use and why not, more aesthetically pleasing API. And just maybe, the effort of having to squeeze an API in a fluent form may lead to better thought out API?

接口有两个方面,实现和使用。在创作方面还有更多工作要做,我同意这一点,但是主要的好处可以在使用方面找到。事实上,对我来说,流畅接口的主要优点是更自然、更容易记住和使用,为什么不,更美观的 API。也许,以流畅的形式压缩 API 的努力可能会导致更好的 API?

As Martin Fowler says in the original article about fluent interfaces:

正如 Martin Fowler 在关于 fluent interfaces 的原始文章中所说:

Probably the most important thing to notice about this style is that the intent is to do something along the lines of an internal DomainSpecificLanguage. Indeed this is why we chose the term 'fluent' to describe it, in many ways the two terms are synonyms. The API is primarily designed to be readable and to flow. The price of this fluency is more effort, both in thinking and in the API construction itself. The simple API of constructor, setter, and addition methods is much easier to write. Coming up with a nice fluent API requires a good bit of thought.

可能关于这种风格最重要的一点是,它的意图是按照内部 DomainSpecificLanguage 的方式做一些事情。事实上,这就是我们选择“流利”一词来描述它的原因,在许多方面这两个术语是同义词。API 的主要设计目的是可读性和流动性。这种流畅性的代价是更多的努力,无论是在思维上还是在 API 构建本身上。构造函数、setter 和加法方法的简单 API 更容易编写。想出一个漂亮流畅的 API 需要深思熟虑。

As in most cases API's are created once and used over and over again, the extra effort may be worth it.

由于在大多数情况下 API 被创建一次并被反复使用,因此额外的努力可能是值得的。

And verbose? I'm all for verbosity if it serves the readability of a program.

和冗长?如果它服务于程序的可读性,我完全赞成冗长。

回答by Abel

No and yes. The basics are a good interface or interfaces for the types that you want to behave fluently. Libraries with extension methods can extend this behavior and return the interface. Extension methods give others the possibility to extend your fluent API with more methods.

不,是的。基础知识是一个或多个类型的良好接口,用于您想要流畅运行的类型。具有扩展方法的库可以扩展此行为并返回接口。扩展方法使其他人可以使用更多方法扩展您的 fluent API。

A good fluent design can be hard and takes a rather long trial and error period to totally finetune the basic building blocks. Just a fluent API for configuration or setup is not that hard.

一个好的流畅设计可能很困难,需要相当长的试错期才能完全微调基本构建块。只是一个用于配置或设置的流畅 API 并不难。

Learning building a fluent API does one by looking at existing APIs. Compare the FluentNHibernate with the fluent .NET APIs or the ICriteria fluent interfaces. Many configuration APIs are also designed "fluently".

学习构建流畅的 API 是通过查看现有 API 来完成的。将 FluentNHibernate 与 fluent .NET APIs 或 ICriteria fluent 接口进行比较。许多配置 API 也是“流畅”设计的。

回答by Chad

KISS: Keep it simple stupid.

吻:保持简单愚蠢。

Fluent design is about one aesthetic design principle used throughout the API. Thou your methodology you use in your API can change slightly, but it is generally better to stay consistent.

流畅设计是整个 API 中使用的一种美学设计原则。您在 API 中使用的方法可能会略有变化,但通常最好保持一致。

Even though you may think 'everyone can use this API, because it uses all different types of methodology's'. The truth is the user would start feeling lost because your consistently changing the structure/data structure of the API to a new design principle or naming convention.

即使您可能认为“每个人都可以使用此 API,因为它使用了所有不同类型的方法论”。事实是,用户会开始感到迷茫,因为您不断地将 API 的结构/数据结构更改为新的设计原则或命名约定。

If you wish to change halfway through to a different design principle eg.. Converting from error codes to exception handling because some higher commanding power. It would be folly and would normally in tail lots of pain. It is better to stay the course and add functionality that your customers can use and sell than to get them to re-write and re-discover all their problems again.

如果您希望中途更改为不同的设计原则,例如……从错误代码转换为异常处理,因为具有更高的控制能力。这将是愚蠢的,通常会在尾巴上很痛苦。最好坚持到底并添加客户可以使用和销售的功能,而不是让他们重新编写和重新发现他们的所有问题。

Following from the above, you can see that there is more at work of writing a Fluent API than meet's the eye. There are psychological, and aesthetic choices to make before beginning to write one and even then the feeling,need, and desire to conform to customers demand's and stay consistent is the hardest of all.

根据以上内容,您可以看到编写 Fluent API 的工作远不止眼前一亮。在开始写作之前,需要做出心理和审美上的选择,即使如此,满足客户需求并保持一致的感觉、需要和愿望是最难的。

回答by badbod99

What is a fluent API

什么是流畅的 API

Wikipedia defines them here http://en.wikipedia.org/wiki/Fluent_interface

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

Why Not to use a fluent interface

为什么不使用流畅的界面

I would suggest not implementing a traditional fluent interface, as it increases the amount of code you need to write, complicates your code and is just adding unnecessary boilerplate.

我建议不要实现传统的流畅接口,因为它增加了您需要编写的代码量,使您的代码复杂化,并且只是添加了不必要的样板。

Another option, do nothing!

另一种选择,什么都不做!

Don't implement anything. Don't provide "easy" constructors for setting properties and don't provide a clever interface to help your client. Allow the client to set the properties however they normally would. In .Net C# or VB this could be as simple as using object initializers.

不要实施任何东西。不要提供用于设置属性的“简单”构造函数,也不要提供巧妙的界面来帮助您的客户。允许客户端以通常的方式设置属性。在 .Net C# 或 VB 中,这可以像使用对象初始值设定项一样简单。

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

So you don't need to create any clever interface in your code, and this is very readable.

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

If you have very complex Setsof properties which must be set, or set in a certain order, then use a separate configuration object and pass it to the class via a separate property.

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

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

回答by sam

MrBlah,

布拉先生,

Though you can write extension methods to write a fluent interface, a better approach is using the builder pattern. I'm in the same boat as you and I'm trying to figure out a few advanced features of fluent interfaces.

虽然您可以编写扩展方法来编写流畅的界面,但更好的方法是使用构建器模式。我和你在同一条船上,我正在尝试找出流畅界面的一些高级功能。

Below you'll see some sample code that I created in another thread

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

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);

回答by Leandro A. Boffi

With a fluent API:

使用流畅的 API:

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

Check out this video http://www.viddler.com/explore/dcazzulino/videos/8/

看看这个视频http://www.viddler.com/explore/dcazzulino/videos/8/

回答by eric

Writting a fluent API it's complicated, that's why I've written Diezelthat is a Fluent API generator for Java. It generates the API with interfaces (or course) to:

编写流畅的 API 很复杂,这就是为什么我编写了Diezel,它是 Java 的 Fluent API 生成器。它生成带有接口(或课程)的 API 以:

  1. control the calling flow
  2. catch generic types (like guice one)
  1. 控制呼叫流程
  2. 捕获泛型类型(如 guice 一)

It generates also implementations.

它还会生成实现。

It's a maven plugin.

这是一个maven插件。

回答by Lukas Eder

While many people cite Martin Fowler as being a prominent exponent in the fluent API discussion, his early design claims actually evolve around a fluent builder patternor method chaining. Fluent APIs can be further evolved into actual internal domain-specific languages. An article that explains how a BNF notation of a grammar can be manually transformed into a "fluent API" can be seen here:

虽然许多人认为 Martin Fowler 是 fluent API 讨论中的杰出代表,但他早期的设计主张实际上是围绕fluent builder 模式方法链发展的。Fluent API 可以进一步演变为实际的内部特定领域语言。可以在此处查看解释如何将语法的 BNF 表示法手动转换为“流畅的 API”的文章:

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

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

It transforms this grammar:

它转换了这个语法:

enter image description here

在此处输入图片说明

Into this Java API:

进入这个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 and C# being somewhat similar, the example certainly translates to your use-case as well. The above technique has been heavily used in jOOQ, a fluent API / internal domain-specific language modelling the SQL language in Java

Java 和 C# 有点相似,该示例当然也适用于您的用例。上述技术已在jOOQ 中大量使用,jOOQ是一种流畅的 API / 内部领域特定语言,在 Java 中对 SQL 语言进行建模

回答by Shavais

This is a very old question, and this answer should probably be a comment rather than an answer, but I think it's a topic worth continuing to talk about, and this response is too long to be a comment.

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

The original thinking concerning "fluency" seems to have been basically about adding power and flexibility (method chaining, etc) to objects while making code a bit more self-explanatory.

关于“流畅性”的最初想法似乎基本上是为对象添加功能和灵活性(方法链等),同时使代码更加不言自明。

For example

例如

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

is less "fluent" than

不如“流利”

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")
    )
;

But to me, the later is not really any more powerful or flexible or self-explanatory than

但对我来说,后者实际上并不比后者更强大、更灵活或更不言自明

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

..in fact I would call this last version easier to create, read and maintain than the previous, and I would say that it requires significantly less baggage behind the scenes, as well. Which to me is important, for (at least) two reasons:

..事实上,我认为最后一个版本比前一个版本更容易创建、阅读和维护,而且我会说它在幕后需要的包袱也少得多。这对我来说很重要,原因(至少)有两个:

1 - The cost associated with creating and maintaining layers of objects (no matter who does it) is just as real, relevant and important as the cost associated with creating and maintaining the code that consumes them.

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

2 - Code bloat embedded in layers of objects creates just as many (if not more) problems as code bloat in the code that consumes those objects.

2 - 嵌入在对象层中的代码膨胀会产生与使用这些对象的代码中的代码膨胀一样多(如果不是更多的话)的问题。

Using the last version means you can add a (potentially useful) property to the Company class simply by adding one, very simple line of code.

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

That's not to say that I feel there's no place for method chaining. I really like being able to do things like (in JavaScript)

这并不是说我觉得没有方法链接的地方。我真的很喜欢能够做这样的事情(在 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 (in the hypothetical case I'm imagining) Done and Fail were additions to the original Ajax object, and were able to be added without changing any of the original Ajax object code or any of the existing code that made use of the original Ajax object, and without creating one-off things that were exceptions to the general organization of the code.

..where(在我想象的假设情况下)Done 和 Fail 是对原始 Ajax 对象的添加,并且能够在不更改任何原始 Ajax 对象代码或任何利用原始的 Ajax 对象,并且没有创建一次性的东西,这些东西是代码的一般组织的例外。

So I have definitely found value in making a subset of an object's functions return the 'this' object. In fact whenever I have a function that would otherwise return void, I consider having it return this.

因此,我确实发现使对象函数的子集返回“this”对象的价值。事实上,每当我有一个会返回 void 的函数时,我都会考虑让它返回 this。

But I haven't yet really found significant value in adding "fluent interfaces" (.eg "Set") to an object, although theoretically it seems like there could be a sort of namespace-like code organization that could arise out of the practice of doing that, which might be worthwhile. ("Set" might not be particularly valuable, but "Command", "Query" and "Transfer" might, if it helped organize things and facilitate and minimize the impact of additions and changes.) One of the potential benefits of such a practice, depending on how it was done, might be improvement in a coder's typical level of care and attention to protection levels - the lack of which has certainly caused great volumes grief.

但是我还没有真正发现将“流畅的接口”(例如“Set”)添加到对象中的重要价值,尽管从理论上讲,实践中似乎可能存在一种类似于名称空间的代码组织这样做,这可能是值得的。(“设置”可能不是特别有价值,但“命令”、“查询”和“传输”可能会帮助组织事物并促进和最小化添加和更改的影响。)这种做法的潜在好处之一,取决于它是如何完成的,可能会提高编码员对保护级别的典型关心和关注水平 - 缺乏这些肯定会引起巨大的悲伤。