在Delphi 中使用流畅的接口有什么优缺点?
流畅的接口应该会增加可读性,但我有点怀疑有一个包含大量链接方法的长LOC.
有编译器问题吗?
有任何调试问题吗?
是否有任何运行时/错误处理问题?
Fluent接口用于例如TStringBuilder,THTMLWriter和TGpFluentXMLBuilder.
更新:
David Heffernan询问我关注哪些问题.我已经考虑过这个问题了,整体问题是"明确指定它是如何完成"与"让编译器决定如何完成"之间的区别.
AFAICS,没有关于编译器如何实际处理链接方法的文档,也没有关于编译器如何处理链式方法的任何规范.
在本文中,我们可以阅读有关编译器如何向声明为函数的方法添加两个额外的var参数,以及标准调用约定在寄存器中放置三个参数以及在堆栈中放置下一个参数的情况.因此,具有2个参数的"流畅函数方法"将使用该堆栈,而具有2个参数的"普通过程方法"仅使用该寄存器.
我们也知道编译器在优化二进制文件方面做了一些魔术(例如字符串作为函数结果,评估顺序,参考本地proc),但有时会给程序员带来惊人的副作用.
因此,内存/堆栈/寄存器管理更复杂以及编译器可能会产生一些无意识的副作用,这对我来说非常臭.因此问题.
在我阅读了答案(非常好的答案)后,我的担忧大打折扣,但我的偏好仍然是相同的:)
我正在编写一个流畅的API来配置和实例化一系列"消息"对象.我有一个消息类型的层次结构.
为了能够在使用流畅的API时访问子类的方法,我使用泛型来参数化子类,并使所有流畅的方法(以"with"开头)返回泛型类型.请注意,我省略了流体方法的大部分主体; 其中有很多配置.
public abstract class Message<T extends Message<T>> {
protected Message() {
}
public T withID(String id) {
return (T) this;
}
}
Run Code Online (Sandbox Code Playgroud)
具体子类同样重新定义泛型类型.
public class CommandMessage<T extends CommandMessage<T>> extends Message<CommandMessage<T>> {
protected CommandMessage() {
super();
}
public static CommandMessage newMessage() {
return new CommandMessage();
}
public T withCommand(String command) {
return (T) this;
}
}
public class CommandWithParamsMessage extends
CommandMessage<CommandWithParamsMessage> {
public static CommandWithParamsMessage newMessage() {
return new CommandWithParamsMessage();
}
public CommandWithParamsMessage withParameter(String paramName,
String paramValue) {
contents.put(paramName, …Run Code Online (Sandbox Code Playgroud) 如果我尝试删除"子"行,我总是会遇到异常.这是一个片段:
using (var context = new CompanyContext())
{
ItemType itemType = context.ItemTypes.FirstOrDefault(i => i.Name == "ServerType");
ItemTypeItem itemTypeItem = itemType.Items.FirstOrDefault(i => i.Name == "DatabaseServer");
itemType.Items.Remove(itemTypeItem);
context.SaveChanges(); <=== exception!
}
Run Code Online (Sandbox Code Playgroud)
该SaveChanges()方法抛出以下异常.
"由于一个或多个外键属性不可为空,因此无法更改关系.当对关系进行更改时,相关的外键属性将设置为空值.如果外键是如果不支持空值,则必须定义新关系,必须为foreign-key属性分配另一个非空值,或者必须删除不相关的对象."
实体配置
public class ItemTypeConfiguration : NamedEntityConfiguration<ItemType>
{
public ConfigurationColumn ParentIDColumn;
public ConfigurationColumn ValidationPatternColumn;
public ItemTypeConfiguration() : base()
{
ParentIDColumn = new ConfigurationColumn() { Name = "ParentID", Ordinal = base.LastOrdinalPosition + 1 };
ValidationPatternColumn = new ConfigurationColumn() { Name = "ValidationPattern", Length = 1024, Ordinal=base.LastOrdinalPosition + 2};
this.Property(t …Run Code Online (Sandbox Code Playgroud) 我有一个类Product和一个复杂的类型AddressDetails
public class Product
{
public Guid Id { get; set; }
public AddressDetails AddressDetails { get; set; }
}
public class AddressDetails
{
public string City { get; set; }
public string Country { get; set; }
// other properties
}
Run Code Online (Sandbox Code Playgroud)
是否可以防止从类AddressDetails内部映射"Country"属性Product?(因为我在Product课堂上永远不需要它)
像这样的东西
Property(p => p.AddressDetails.Country).Ignore();
Run Code Online (Sandbox Code Playgroud) 我刚刚看到Java的一个巨大的正则表达式让我想到了一般的正则表达式的可维护性.我相信大多数人 - 除了一些坏蛋perl贩子 - 会同意正则表达式难以维持.
我在考虑如何解决这种情况.到目前为止,我最有希望的想法是使用流畅的界面.举个例子,而不是:
Pattern pattern = Pattern.compile("a*|b{2,5}");
Run Code Online (Sandbox Code Playgroud)
一个人可以写这样的东西
import static util.PatternBuilder.*
Pattern pattern = string("a").anyTimes().or().string("b").times(2,5).compile();
Pattern alternative =
or(
string("a").anyTimes(),
string("b").times(2,5)
)
.compile();
Run Code Online (Sandbox Code Playgroud)
在这个非常简短的例子中,创建正则表达式的常用方法对于任何平庸的有才华的开发人员来说仍然是可读的.但是,请考虑那些填充两行或更多行的怪异表达式,每行包含80个字符.当然,(冗长)流畅的界面需要几行而不是只有两行,但我相信它会更具可读性(因此可维护).
现在我的问题:
你知道正则表达式的任何类似方法吗?
你是否同意这种方法比使用简单的字符串更好?
你会如何设计API?
你会在你的项目中使用这样一个整洁的实用程序吗?
你认为这会很有趣吗?;)
编辑: 想象一下,可能存在比简单构造更高级别的方法,我们都没有来自正则表达式,例如
// matches aaaab@example.com - think of it as reusable expressions
Pattern p = string{"a").anyTimes().string("b@").domain().compile();
Run Code Online (Sandbox Code Playgroud)
编辑 - 评论的简短摘要:
RegexBuddy - 花30欧元让你的代码可读(wtf?!这种产品的纯粹存在证明了我的论文是正确的 - 我们今天所知道的正则表达式是坏事(tm))
Martin Fowler的方法(仍然远非完美)
有趣的是,大多数人都认为正则表达式仍然存在 - 虽然它需要工具来阅读它们,聪明的家伙想办法让它们可维护.虽然我不确定流畅的界面是最好的方法,但我确信有些聪明的工程师 - 我们呢?;) - 应该花一些时间让正则表达式成为过去 - 这已经足够让他们和我们在一起已有50年了,你不觉得吗?
开放的BOUNTY
对于正则表达式的新方法,赏金将被授予最佳想法(无需代码).
编辑 - 一个很好的例子:
这是我正在谈论的那种模式 - 对能够翻译它的第一个人的额外荣誉 …
我最近正在观看关于如何创建流畅的DSL的网络直播,我不得不承认,我不明白为什么会使用这种方法(至少对于给定的例子).
网络广播提供了一个图像大小调整类,它允许您指定输入图像,调整大小并使用以下语法将其保存到输出文件(使用C#):
Sizer sizer = new Sizer();
sizer.FromImage(inputImage)
.ToLocation(outputImage)
.ReduceByPercent(50)
.OutputImageFormat(ImageFormat.Jpeg)
.Save();
Run Code Online (Sandbox Code Playgroud)
我不明白这是如何比采用一些参数的"传统"方法更好:
sizer.ResizeImage(inputImage, outputImage, 0.5, ImageFormat.Jpeg);
Run Code Online (Sandbox Code Playgroud)
从可用性的角度来看,这似乎更容易使用,因为它清楚地告诉您该方法期望作为输入.相比之下,使用流畅的界面,没有什么可以阻止你省略/忘记参数/方法调用,例如:
sizer.ToLocation(outputImage).Save();
Run Code Online (Sandbox Code Playgroud)
关于我的问题:
1 -是否有某种方法可以提高流畅界面的可用性(即告诉用户他应该做什么)?
2 -这种流畅的界面方法是否只是C#中现有的命名方法参数的替代?命名参数会使流畅的接口过时,例如类似Objective-C提供的东西:
sizer.Resize(from:input, to:output, resizeBy:0.5, ..)
Run Code Online (Sandbox Code Playgroud)
3 -流畅的界面是否过度使用仅仅是因为它们目前很受欢迎?
4 -或者它只是一个被选为网络广播的坏榜样?在这种情况下,请告诉我这种方法的优点是什么,使用它的意义何在.
顺便说一句:我知道jquery,看看它有多容易,所以我不是在寻找关于那个或其他现有例子的评论.
我正在寻找一些(一般)评论来帮助我理解(例如)何时实现流畅的界面(而不是经典的类库),以及实现它时需要注意的事项.
我试图继承str对象,并添加几个方法.我的主要目的是学习如何做到这一点.我被困在哪里,我是否应该在元类中继承字符串,并使用该元或子类str直接创建我的类?
而且,我想我需要以__new__()某种方式实现,因为,我的自定义方法将修改我的字符串对象,并将返回新的mystr obj.
我的类的方法应该可以使用str方法完全链接,并且应该在自定义方法修改它时始终返回一个新的我的类实例.我希望能够做到这样的事情:
a = mystr("something")
b = a.lower().mycustommethod().myothercustommethod().capitalize()
issubclass(b,mystr) # True
Run Code Online (Sandbox Code Playgroud)
我希望拥有它拥有的所有能力str.例如,a = mystr("something")然后我想使用它,如a.capitalize().mycustommethod().lower()
我的理解是,我需要实施__new__().我想是这样的,因为,字符串方法可能会尝试创建新的str实例.所以,如果我覆盖__new__(),他们应该会返回我的自定义str类.但是,__init__()在这种情况下,我不知道如何将参数传递给我的自定义类的方法.我想我需要使用type()才能在__new__()方法中创建一个新实例吗?
我正在尝试为一些构建器类创建一个抽象基类,这样我就可以轻松地在Builder实现之间重用代码.我希望我的构建器支持方法链接,因此方法必须返回最具体类型的"this"实例.我想我可以用泛型来做这件事.不幸的是,如果不使用不安全的操作,我就无法做到这一点.可能吗?
下面是我如何尝试它(以及它如何工作)的示例代码.我想避免在"foo()"中投射到T(导致未经检查的警告),这可以做到吗?
public class Builders
{
public static void main( final String[] args )
{
new TheBuilder().foo().bar().build();
}
}
abstract class AbstractBuilder<T extends AbstractBuilder<?>>
{
public T foo()
{
// set some property
return (T) this;
}
}
class TheBuilder extends AbstractBuilder<TheBuilder>
{
public TheBuilder bar()
{
// set some other property
return this;
}
public Object build()
{
return new Object();
}
}
Run Code Online (Sandbox Code Playgroud) 以下是两种类似的流畅API配置:
和很多()
modelBuilder.Entity<Country>()
.HasRequired(cou => cou.Currency)
.WithMany()
.WillCascadeOnDelete(false);
Run Code Online (Sandbox Code Playgroud)
WithOptional()
modelBuilder.Entity<Country>()
.HasRequired(cou => cou.Currency)
.WithOptional()
.WillCascadeOnDelete(false);
Run Code Online (Sandbox Code Playgroud)
我在这里要表达的是:每个都Country需要具体Currency,但是Currency可以分配给零,一个或多个国家.
我必须使用上述哪一项陈述?或者换句话说:究竟有什么区别.WithMany()和.WithOptional()运营商?
如何使用Entity Framework Code-First将数组的双精度数据存储到数据库中,而不影响现有代码和体系结构设计?
我查看了Data Annotation和Fluent API,我还考虑将double数组转换为字节字符串,并将该字节存储到自己的列中的数据库中.
我无法使用public double[] Data { get; set; }Fluent API 访问该属性,我得到的错误消息是:
该类型
double[]必须是非可空值类型才能将其用作参数"T".
Data存储的类成功存储在数据库中,以及与此类的关系.我只是错过了Data专栏.
fluent-interface ×10
c# ×3
java ×3
generics ×2
.net ×1
api-design ×1
builder ×1
delphi ×1
dsl ×1
python ×1
readability ×1
regex ×1
subclassing ×1