C# 什么';DSLs/fluent接口的要点是什么

C# 什么';DSLs/fluent接口的要点是什么,c#,dsl,fluent-interface,api-design,C#,Dsl,Fluent Interface,Api Design,我最近看了一个关于的网络广播,我不得不承认,我不明白为什么会使用这种方法(至少对于给定的示例) 网络广播提供了一个图像大小调整类,该类允许您使用以下语法(使用C#)指定输入图像、调整其大小并将其保存到输出文件: 我不明白这怎么会比采用一些参数的“传统”方法更好: sizer.ResizeImage(inputImage, outputImage, 0.5, ImageFormat.Jpeg); 从可用性的角度来看,这似乎更易于使用,因为它清楚地告诉您该方法作为输入的期望值。相比之下,使用flu

我最近看了一个关于的网络广播,我不得不承认,我不明白为什么会使用这种方法(至少对于给定的示例)

网络广播提供了一个图像大小调整类,该类允许您使用以下语法(使用C#)指定输入图像、调整其大小并将其保存到输出文件:

我不明白这怎么会比采用一些参数的“传统”方法更好:

sizer.ResizeImage(inputImage, outputImage, 0.5, ImageFormat.Jpeg);
从可用性的角度来看,这似乎更易于使用,因为它清楚地告诉您该方法作为输入的期望值。相比之下,使用fluent界面,没有什么可以阻止您忽略/忘记参数/方法调用,例如:

sizer.ToLocation(outputImage).Save();
下面是我的问题:

1-是否有办法提高流畅界面的可用性(即告诉用户他希望做什么)

2-这种流畅的接口方法是否只是C#中不存在的命名方法参数的替代品?命名参数是否会使fluent接口过时,例如objective-C提供的类似功能:

sizer.Resize(from:input, to:output, resizeBy:0.5, ..)
3-流畅的界面是否仅仅因为当前流行而被过度使用

4-还是选择了一个糟糕的例子进行网络广播?在这种情况下,请告诉我这种方法的优点是什么,在哪里使用它是有意义的

顺便说一句:我知道jquery,并且知道它有多简单,所以我不想找关于它或其他现有示例的评论


我更多的是寻找一些(一般性的)评论来帮助我理解(例如)什么时候实现一个流畅的接口(而不是一个经典的类库),以及在实现一个接口时要注意什么。

这是一种实现方法

对于只会一遍又一遍地操作同一项的对象,它并没有什么问题。考虑C++流:它们是这个接口的终极。每个操作都会再次返回流,因此您可以将另一个流操作链接在一起

如果你在做LINQ,一遍又一遍地操纵一个对象,这是有意义的

然而,在您的设计中,您必须小心。如果你想中途偏离,行为应该是什么?(即

如果obj2是原始对象的75%,那么这意味着您每次都要制作对象的完整副本(在许多情况下有它的优点,例如,如果您尝试制作同一对象的两个实例,但略有不同)

如果这些方法只是操纵原始对象,那么这种语法有点不真实。这些是对对象的操纵,而不是创建更改的对象的操纵

并非所有类都是这样工作的,进行这种设计也没有意义。例如,这种设计风格在硬件驱动程序或GUI应用程序核心的设计中几乎没有用处。只要设计只涉及操作一些数据,这种模式就不是一种坏模式。

考虑:

sizer.ResizeImage(inputImage, outputImage, 0.5, ImageFormat.Jpeg);
如果使用不太清晰的变量名会怎样:

sizer.ResizeImage(i, o, x, ImageFormat.Jpeg);
假设您已经打印出了这段代码,因为您没有访问方法签名的权限,所以很难推断这些参数是什么

通过流畅的界面,这一点更加清晰:

 sizer.FromImage(i)
 .ToLocation(o)
 .ReduceByPercent(x)
 .OutputImageFormat(ImageFormat.Jpeg)
 .Save();
此外,方法的顺序并不重要。这相当于:

 sizer.FromImage(i)
 .ReduceByPercent(x)
 .OutputImageFormat(ImageFormat.Jpeg)
 .ToLocation(o)
 .Save();
此外,您可能有输出图像格式的默认值和缩减,因此这可能成为:

 sizer.FromImage(i)
 .ToLocation(o)
 .Save();
这将需要重载构造函数来实现相同的效果

2-这是流畅的接口方法吗 只是一个替代品 中现有的命名方法参数 C#?命名的参数是否会使语言流畅 过时的接口,例如某物 类似的objective-C提供:

sizer.Resize(from:input, to:output, resizeBy:0.5, ..)
是和否。fluent界面为您提供了更大的灵活性。使用命名参数无法实现的功能是:

sizer.FromImage(i)
 .ReduceByPercent(x)
 .Pixalize()
 .ReduceByPercent(x)
 .OutputImageFormat(ImageFormat.Jpeg)
 .ToLocation(o)
 .Save();
流体界面中的FromImage、ToLocation和OutputImageFormat对我来说有点异味。相反,我会按照这些思路做一些事情,我认为这会更加清晰

 new Sizer("bob.jpeg") 
 .ReduceByPercent(x)
 .Pixalize()
 .ReduceByPercent(x)
 .Save("file.jpeg",ImageFormat.Jpeg);
Fluent接口与许多编程技术存在相同的问题,它们可能被误用、过度使用或未充分使用。我认为,如果有效地使用这种技术,它可以创建更丰富、更简洁的编程模型。即使StringBuilder也支持它

var sb = new StringBuilder(); 
sb.AppendLine("Hello")
 .AppendLine("World"); 

我想说,流畅的界面有点过头了,我认为您只选择了一个这样的例子

当你用它构建一个复杂的模型时,我发现流畅的接口特别强大。对于模型,我指的是一个实例化对象的复杂关系。然后,流畅的接口是一种引导开发人员正确构建语义模型实例的方法。这样一个流畅的接口是一种很好的方法来分离从您用来构建模型的“语法”中提取模型的机制和关系,基本上屏蔽了来自最终用户的细节,并将可用动词减少到可能仅与特定场景相关的动词

你的例子似乎有点过分了

我最近在Windows窗体的SplitterContainer上做了一些流畅的接口。可以说,控件层次结构的语义模型要正确构建有点复杂。通过提供一个小的fluent API,开发人员现在可以声明性地表达他的SplitterContainer应该如何工作。用法如下

var s = new SplitBoxSetup();
s.AddVerticalSplit()
 .PanelOne().PlaceControl(()=> new Label())
 .PanelTwo()
 .AddHorizontalSplit()
 .PanelOne().PlaceControl(()=> new Label())
 .PanelTwo().PlaceControl(()=> new Panel());
form.Controls.Add(s.TopControl);
现在,我将复杂的控制层次结构机制简化为与当前问题相关的两个动词

希望这有帮助

您应该阅读Eric Evans的文章,了解为什么DSL被认为是好的设计选择


这本书充满了好的例子、最佳实践建议和设计模式。强烈推荐。

可以在流畅的界面上使用变体来实现enforc
var s = new SplitBoxSetup();
s.AddVerticalSplit()
 .PanelOne().PlaceControl(()=> new Label())
 .PanelTwo()
 .AddHorizontalSplit()
 .PanelOne().PlaceControl(()=> new Label())
 .PanelTwo().PlaceControl(()=> new Panel());
form.Controls.Add(s.TopControl);