procedure frmMain.ButtonClick(Sender TComponent);
begin
with foo do
begin
SetCaption('click me!')
SetTag(42);
end
end;
型 但关键分歧依然存在。上面的代码是 two statements implicitly using foo vs the single statement explicitly calling foo in the fluent variant. 一个显著的额外区别是with可以与 * 多个符号 * 一起使用(如果你绝对决心让你和你的同事的生活变得困难:))。这有(希望)明显的潜在可能性,使得很难(一眼)分辨with作用域中的哪些引用解析为哪些符号。 另一方面,流畅的API总是从某个对象开始,链总是从那个对象继续。对于你正在处理的符号,从来没有任何疑问或怀疑。 只是为了好玩,我们可以通过观察with和流畅度是正交的,看看这两者如何一起使用来创造一个真实的的怪物:
procedure frmMain.ButtonClick(Sender TComponent);
begin
with foo do
begin
SetCaption('click me!')
.SetTag(42);
end;
end;
type
TFooHelper = class helper for TFoo
function turnOn : TFoo;
function Caption( const str : string ) : TFoo;
end;
function TFooHelper.turnOn : TFoo;
begin
self.turnOn; // Self refers to TFoo type, not TFooHelper
Result := self;
end;
function TFooHelper.Caption( const str : string ) : TFoo;
begin
self.Caption := str;
Result := self;
end;
2条答案
按热度按时间pb3skfrl1#
有一些(非常,非常少)用例中,
with
使用起来是“安全的”,但应该避免使用。因此,我不打算(详细)详细讨论为什么你应该或不应该使用
with
,并坚持回答你问题的第二部分:显然误解了with
和fluent API在某种程度上是直接相关的,而实际上它们并不相关。with
with
接受一个或多个符号并创建一个作用域,其中这些符号优先于包含作用域中同名的其他符号:因此,如果我们想象我们正在某个表单上编写代码,并且需要在该表单上设置某个组件的多个属性,其中一些属性也存在(即,使用相同的名称),我们可以使用
with
创建一个作用域,在该作用域中,对那些属性的非限定引用解析为组件,而不是表单:字符串
另一种方法是不使用
with
,而是使用对foo
属性的限定引用:型
这显然不是一个“流畅”的API。那是什么
Fluent API
流畅API是一个术语,用于描述一种模式,其中对象的方法返回对该对象的引用,允许通过链接它们来调用其他方法。
坚持假设的
foo
组件,如果我们想象该组件的开发人员提供了一个流畅的API来设置属性,而不是(或者也可能)直接将它们公开为属性,那么我们可以想象可能编写类似于下面的代码:型
SetCaption()
和SetTag()
返回调用它们的对象,允许对该对象的进一步调用链接在一起。表面上看起来与
with
模式相似,虽然不那么“罗嗦”,但有两个关键区别:with
方法涉及 * 多个 * 语句1.流畅的API调用是对 * 显式地 * 标识的对象进行的;使用
with
进行的调用 * 隐式 * 标识目标对象with
和fluent API的混淆可能源于with
* 与 * a fluent api * 结合使用 * 本质上只是语法的变体:型
但关键分歧依然存在。上面的代码是 two statements implicitly using
foo
vs the single statement explicitly callingfoo
in the fluent variant.一个显著的额外区别是
with
可以与 * 多个符号 * 一起使用(如果你绝对决心让你和你的同事的生活变得困难:))。这有(希望)明显的潜在可能性,使得很难(一眼)分辨with
作用域中的哪些引用解析为哪些符号。另一方面,流畅的API总是从某个对象开始,链总是从那个对象继续。对于你正在处理的符号,从来没有任何疑问或怀疑。
只是为了好玩,我们可以通过观察
with
和流畅度是正交的,看看这两者如何一起使用来创造一个真实的的怪物:型
那么相似性在哪里呢?
唯一真实的的相似之处是,流畅API提供了几乎相同的好处,即不必限定每个调用(或者更准确地说,* 通过使每个后续调用的限定成为前一个调用的一部分 *)。
with
的一个大问题是,迄今为止的调试器和IDE工具仍然无法像编译器那样解析符号,这可能会导致在尝试调试with
语句中的代码时出现重大问题。在调试器中检查with
作用域值可能会解析到错误的符号,从而给出错误的值。Fluent API有自己的调试挑战,最明显的挑战是Fluent API调用的链式序列是一个语句,在该语句上只能放置一个断点。
应该用哪一种?
作为API的消费者...
如果你面对的是一个没有流畅API的场景,那么在
with
和流畅api之间别无选择。仅决定是否使用with
。这是一个长期而激烈的辩论。对此的简短回答是(IMHO):* 不要使用
with
,除非/直到你确信地确定了(非常,非常少数)边缘情况,这样做是安全和有益的 *。同时,默认为“不”。如果有一个流利的API,那就使用它。它提供了非常相似的语法速记,没有
with
的缺陷,尽管在断点方面有一些粒度的缺失,正如前面提到的。当然,第三种选择是既不使用
with
也不使用fluency,而只是引用fluent API链的“根”,并在单独的语句中使用它,而不是链接调用。作为API的开发者
想想你的代码将如何被使用,并考虑一个流畅的API是否有意义。“Builder”类型的API非常常见地使用Fluent API,也有其他用例,但有些Fluent API非常不直观,使用起来可能会很麻烦;它们不是万灵药。
如果你在API中遇到了一个边缘情况,不可能使用流畅性,那么你就有可能让你的用户处于一种不舒服的境地,在某些方面使用流畅性,而在其他方面却无法使用,这可能会让你非常沮丧,因为你必须记住api的哪些部分是以何种方式工作的。
希望能帮上忙。
sirbozc52#
上面的解释很好,但从你的问题中看不出你在寻找什么。这里有一个稍微不同的看法。
假设你有一个类TFoo,它有几个不同的方法。在你的代码中,你可能有机会写这样的东西:
字符串
使用“with”可以很容易地做到:
型
在这种情况下,使用“流畅”方法不会直接起作用,因为这些方法都不可能返回对foo的引用。(你可能会注意到没有一个VCL方法是为此而设计的。)所以你需要重写你需要的那些方法(或者整个类),这样你就可以做下面的事情:
型
这并不是很难,你可以为任何类型的'foo'创建一个Helper类。每一个都需要是一个返回基类型的函数:
型
等等。