Oop 流畅的接口和漏洞百出的抽象

Oop 流畅的接口和漏洞百出的抽象,oop,fluent-interface,leaky-abstraction,Oop,Fluent Interface,Leaky Abstraction,什么是流畅的界面?我找不到一个很好的定义,但我得到的只是一种我不太熟悉的语言(例如C++)中的长代码示例 还有,什么是泄漏抽象 感谢Eric Evans创造的流畅界面,这只是方法链接的另一个名称。马丁·福勒(Martin Fowler)就这一主题写了一篇文章,但大致如下: m_Window = window::with() .width(l_Width) .height(l_Height) .title("default window") .left(200)

什么是流畅的界面?我找不到一个很好的定义,但我得到的只是一种我不太熟悉的语言(例如C++)中的长代码示例

还有,什么是泄漏抽象


感谢Eric Evans创造的流畅界面,这只是方法链接的另一个名称。马丁·福勒(Martin Fowler)就这一主题写了一篇文章,但大致如下:

m_Window = window::with()
    .width(l_Width)
    .height(l_Height)
    .title("default window")
    .left(200)
    .top(200)
.create();

Fluent接口通常用于创建不支持它们的命名参数(例如C++中的命名参数习惯用法),或者在特定于域的语言中使代码读得更流畅。 我见过它们被用于从图像处理库到正则表达式库、3D库的所有方面。其他示例包括树结构、列表或其他数据结构的构造。所有需要构造复杂对象(参数负载)的东西都可以利用Fluent接口使其更具可读性。例如,将上一个示例与CreateWindow函数调用进行比较:

 ::CreateWindow(
      "Window class", 
      "Window title", 
      dwStyle, X, Y, 
      nWidth, nHeight, 
      hWndPant, hMenu, 
      hInstance, NULL
 );

这里有一个常规的每日界面:

public interface NotFluent
{
  void DoA();
  void DoB();
  void DoC();
}
public interface Fluent
{
  Fluent DoA();
  Fluent DoB();
  Fluent DoC();
}
Car car = Car.describedAs()
  .box()
  .length(50.5)
  .type(Type.INSULATED)
  .includes(Equipment.LADDER)
  .lining(Lining.CORK);
这是一个流畅的界面:

public interface NotFluent
{
  void DoA();
  void DoB();
  void DoC();
}
public interface Fluent
{
  Fluent DoA();
  Fluent DoB();
  Fluent DoC();
}
Car car = Car.describedAs()
  .box()
  .length(50.5)
  .type(Type.INSULATED)
  .includes(Equipment.LADDER)
  .lining(Lining.CORK);
最明显的区别是,当我们返回一个void时,我们返回的是接口类型的实例。可以理解的是,返回的接口是当前实例,而不是相同类型的新实例。当然,这是不可强制执行的,在不可变对象(如字符串)的情况下,它是一个不同的实例,但可以被视为仅更新的同一实例

以下是它们的使用示例:

NotFluent foo = new NotFluentImpl();
foo.DoA();
foo.DoB();
foo.DoC();

Fluent bar = new FluentImpl();
bar.DoA().DoB().DoC();

注意,当链接不同的调用时,fluent接口更容易使用。IRL,检查Linq扩展方法,以及每个调用是如何设计为流入另一个调用的。没有一个方法返回void,即使它是一个有效的结果。

在fluent接口中,对象的方法将返回对对象的引用,以便将方法调用链接在一起

例如,在NValidate中,我这样做是为了简化参数验证:

public City GetCity(string zipCode)
{
   zipCode.Assert("zipCode").IsNotNullOrEmpty().HasLength(5,10).Matches("\\d[5]-\\d[4]");
   // Continue processing
}

不过,我不能说是漏洞百出的抽象。

流畅的接口是一种API,它允许您编写读起来或多或少像普通英语的代码。例如:

Find.All.Questions(Where.IsAnswered == true);
方法链接通常作为实现的一部分使用,但它还有更多的功能。引述:

我还注意到一个常见的误解——许多人似乎将流畅的接口等同于方法链接。当然,链接是一种用于流畅界面的常见技术,但真正的流畅远不止于此


它也经常被称为内部接口,因为语法类似于DSL,但它是在宿主语言内部实现的,而不是由解析器处理。

如果为副作用而执行的方法返回
self
,则面向对象的接口是流畅的,这样就可以将这些方法链接在一起


我第一次遇到fluent接口是在1990年,当时Modula-3接口警察(我不是编造这个)要求所有初始化方法返回初始化的对象。我相信这种用法早于“流畅的界面”这一术语的出现。

泄漏的抽象是一种抽象,在这种抽象中,潜在现实的细节常常“泄漏”

多多少少,但有时抽象与潜在现实的契合度太差,以至于造成的伤害大于帮助

抽象中“泄漏”的一个简单示例可能是常见的浮点类型。它似乎代表一般实数,您可以使用它来执行基本计算。但有时您会遇到这样一种情况,即1/3*3!=1或1+10^-20=1。这就是实际的实现细节泄露,抽象中断的时候。

谢谢大家

很好的描述

我对流畅界面的想法是为了可读性。我总是可以阅读一系列方法,以及其中一个方法与上一个/下一个方法的关系

例如,就像张贴验证示例的海报一样(我编写了与之前类似的代码)。

在他的《高效程序员》一书中很好地解释并给出了流畅的界面示例

具有getter/setter的传统对象或“bean”:

Car car = new CarImpl();
MarketingDescription des = new MarketingDescriptionImpl();
desc.setType("Box");
desc.setSubtype("Insulated");
desc.setAttribute("length", "50.5");
desc.setAttribute("ladder", "yes");
desc.setAttribute("lining type", "cork");
car.setDescription(desc);
通过流畅的界面满足同样的需求:

public interface NotFluent
{
  void DoA();
  void DoB();
  void DoC();
}
public interface Fluent
{
  Fluent DoA();
  Fluent DoB();
  Fluent DoC();
}
Car car = Car.describedAs()
  .box()
  .length(50.5)
  .type(Type.INSULATED)
  .includes(Equipment.LADDER)
  .lining(Lining.CORK);

我已经有一段时间没有做任何与web相关的开发了,但从jQuery文档来看,似乎是这样。这是方法链接,而不是流畅的接口。参见上面Rasmus回答的引语“当然,链接是流畅界面使用的一种常见技术,但真正的流畅远不止于此……”这应该作为两个独立的问题发布。而且。。fluent接口的优缺点是什么?这个例子不是简单的方法链接吗?福勒注意到。上面的代码有什么特性使它成为一个流畅的界面,而不仅仅是方法链接?@Pacerier如果你有兴趣分割这根头发(而引用的内容根本没有像作者所想象的那样有区别),那么与其对每个答案都加注释,为什么不自己回答呢?或者,如果你想了解更多关于如何分割头发的知识,可以问一个新问题,集中讨论两种头发之间的区别。就我个人而言,我看不到太多。绝对不足以做出有价值的区分。我看到它们密不可分地联系在一起。只是有时候模式会出现,有时候不会。我想我对美学不感兴趣。关于“为什么不自己回答它”,我没有答案,因此产生了问题。这不是“吹毛求疵”,很明显FluentInterface不等于方法链接。@Pacerier哦,这没有任何意义。你声称很明显他们是不同的,但你自己却没有答案?我相信你在开玩笑。您好,先生。我不明白为什么rep的数量与t相关