C# 创建流畅的API

C# 创建流畅的API,c#,fluent,C#,Fluent,如何创建一个本质上流畅的API 这主要是使用扩展方法吗?比我以前解释得更好 编辑,无法在评论中压缩此内容 接口有两个方面:实现和使用。在创作方面还有更多的工作要做,我同意这一点,但是主要的好处可以在事物的使用方面找到。事实上,对我来说,流畅界面的主要优点是更自然、更容易记忆和使用,为什么不呢,更美观的API。也许,以流畅的形式压缩API的努力可能会导致更好的思考API 正如Martin Fowler在书中所说: 也许对我来说最重要的事情 关于此样式的注意事项是 目的是沿着道路做一些事情 内线 域

如何创建一个本质上流畅的API

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

比我以前解释得更好

编辑,无法在评论中压缩此内容

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

正如Martin Fowler在书中所说:

也许对我来说最重要的事情 关于此样式的注意事项是 目的是沿着道路做一些事情 内线 域特定语言。确实如此 为什么我们选择“流利”一词来 用多种方式描述这两种情况 术语是同义词。API是 主要设计为可读和 流动。这种流利的代价是 更多的努力,无论是在思想上还是在实践上 API构造本身。这个 构造函数、setter和 加法要容易得多 写说一口流利的英语 API需要仔细考虑

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


而且冗长?我完全赞成冗长,如果它有助于程序的可读性。

否和是。基础是一个很好的接口,用于您想要流畅地操作的类型。具有扩展方法的库可以扩展此行为并返回接口。扩展方法为其他人提供了使用更多方法扩展fluent API的可能性

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


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

KISS:保持简单

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

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

如果您希望中途更改为不同的设计原则,例如。。从错误代码转换为异常处理,因为某些更高的命令权限。这将是愚蠢的,通常会带来很多痛苦。与其让客户重新编写和重新发现所有问题,不如坚持到底,添加客户可以使用和销售的功能


从上面的内容中,您可以看到,编写一个流畅的API所做的工作比meet的eye还要多。在开始编写一个API之前,需要做出心理和美学上的选择,即使这样,满足客户需求并保持一致的感觉、需求和愿望也是最难的。

什么是流畅的API

维基百科在这里定义了它们

为什么不使用流畅的界面

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

另一种选择,什么也不做

不要实施任何事情。不要提供“简单”的构造函数来设置属性,也不要提供一个聪明的界面来帮助你的客户。允许客户端以正常方式设置属性。在.NETC#或VB中,这可能与使用一样简单

因此,您不需要在代码中创建任何聪明的接口,这是非常可读的

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

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

虽然可以编写扩展方法来编写流畅的界面,但更好的方法是使用生成器模式。我和你处于同一条船上,我正试图找出fluent接口的一些高级特性

下面您将看到我在中创建的一些示例代码

使用流畅的API:

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

看看这段视频,写一个fluent-API很复杂,这就是为什么我写了一个fluent-API生成器。它生成带有接口(或课程)的API,以:

  • 控制调用流
  • 捕获泛型类型(如guice one)
  • 它还生成实现


    这是一个maven插件。

    虽然许多人认为Martin Fowler是fluent API讨论中的杰出代表,但他早期的设计主张实际上是围绕or展开的。Fluent API可以进一步发展为实际的API。下面可以看到一篇文章,解释如何将语法的BNF符号手动转换为“fluent 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和C#有点相似,这个例子当然也会转化为您的用例。上述技术在Java中大量使用,一种流畅的API/内部领域特定语言,用Java建模SQL语言这是一个非常古老的问题,这个答案应该是一个注释而不是答案,但我认为这是一个值得继续讨论的话题,而且这个回答太长,不能作为注释

    关于“流利性”的最初想法似乎基本上是为了增加力量
    myCar.SetColor(Color.Blue).SetName("Aston Martin");
    
    // 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();
    }
    
    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." }
    };
    
    var _this = this;
    Ajax.Call({
        url: '/service/getproduct',
        parameters: {productId: productId},
    )
    .Done(
        function(product){
            _this.showProduct(product);
        }
    )
    .Fail(
        function(error){
            _this.presentError(error);
        }
    );