C# 什么';DSLs/fluent接口的要点是什么
我最近看了一个关于的网络广播,我不得不承认,我不明白为什么会使用这种方法(至少对于给定的示例) 网络广播提供了一个图像大小调整类,该类允许您使用以下语法(使用C#)指定输入图像、调整其大小并将其保存到输出文件: 我不明白这怎么会比采用一些参数的“传统”方法更好: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
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);