【问题标题】:Fluent interfaces and leaky abstractions流畅的接口和有漏洞的抽象
【发布时间】:2010-09-30 19:30:03
【问题描述】:

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

另外,什么是泄漏抽象?

谢谢

【问题讨论】:

  • 这应该作为两个单独的问题发布。

标签: oop fluent-interface leaky-abstraction


【解决方案1】:

泄漏抽象是底层现实的细节经常“泄漏”的抽象。

All abstractions lie 或多或少,但有时抽象与底层现实非常不匹配,导致弊大于利。

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

【讨论】:

    【解决方案2】:

    Neal Ford 在他的“高效程序员”一书中很好地解释和提供了流畅的接口示例。

    带有 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);
    

    用流畅的界面满足同样的需求:

    Car car = Car.describedAs()
      .box()
      .length(50.5)
      .type(Type.INSULATED)
      .includes(Equipment.LADDER)
      .lining(Lining.CORK);
    

    【讨论】:

      【解决方案3】:

      谢谢大家。

      很好的描述。

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

      例如就像发布验证示例的发帖人(我之前写过类似的代码)。

      【讨论】:

        【解决方案4】:

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

        Find.All.Questions(Where.IsAnswered == true);
        

        方法链通常用作实现的一部分,但它还有更多功能。引用Fowler:

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

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

        【讨论】:

        • 还有..流畅界面的优缺点是什么?
        【解决方案5】:

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

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

        【讨论】:

        • 其实当 OP 说“流畅的界面”时,他很可能指的是 Fowler 对FluentInterface 的定义。不仅仅是一般的 fluent 接口。
        【解决方案6】:

        Eric Evans 创造了一个流畅的接口术语,它只是方法链的另一个名称。 Martin Fowler 就这个主题写了一篇couplearticles,但大致是这样的:

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

        Fluent 接口通常用于在不支持它们的语言中创建命名参数(例如 C++ 中的命名参数惯用语),或者在特定领域语言中创建命名参数,以使代码更流畅地阅读。

        我已经看到它们被用于从图像处理库到正则表达式库和 3D 库的所有内容。其他示例包括树结构、列表或其他数据结构的构建。所有需要构造复杂对象(加载参数)的东西都可以使用 Fluent Interfaces 使其更具可读性。例如,将前面的示例与 CreateWindow 函数调用进行比较:

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

        【讨论】:

        • 好久没做web相关的开发了,但是从jquery文档来看,好像是的。
        • 这是方法链,不是流利的接口。请参阅上面 Rasmus 的回答中的引述 “当然,链接是与流畅接口一起使用的常用技术,但真正的流畅性远不止于此......”
        【解决方案7】:

        在流畅的接口中,对象的方法将返回对该对象的引用,以便您可以将方法调用链接在一起。

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

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

        不过,我不能谈论有漏洞的抽象。

        【讨论】:

          【解决方案8】:

          这是一个常规的日常界面:

          public interface NotFluent
          {
            void DoA();
            void DoB();
            void DoC();
          }
          

          这是一个流畅的界面:

          public interface Fluent
          {
            Fluent DoA();
            Fluent DoB();
            Fluent DoC();
          }
          

          最明显的区别是当我们返回一个 void 时,我们返回的是一个接口类型的实例。可以理解的是,返回的接口是CURRENT INSTANCE,而不是同类型的新实例。当然,这是不可强制执行的,在不可变对象(如字符串)的情况下,它是一个不同的实例,但可以认为是同一个实例,只是更新了。

          以下是它们的使用示例:

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

          请注意,在链接不同的调用时,流畅的界面更易于使用。 IRL,请查看 Linq 扩展方法以及每个调用是如何设计为流入另一个调用的。没有任何方法返回 void,即使它是一个有效的结果。

          【讨论】:

          • 这个例子不就是简单的方法链吗?福勒笔记"many people seem to equate fluent interfaces with Method Chaining"。上面的代码有什么属性使它成为一个流畅的接口而不是单纯的方法链接?
          • @Pacerier 如果您有兴趣分裂这根头发(并且该参考文献根本没有涉及作者应该看到的区别),那么不要在每个答案上都丢掉 cmets,为什么不自己回答?或者,如果您想了解更多关于如何拆分头发的信息,请提出一个新问题,重点关注两者之间的区别。就个人而言,我看的不多。绝对不足以做出有价值的区分。我看到它们有着千丝万缕的联系。只是有时模式会出现,有时不会。我猜我对美学不感兴趣。
          • Re “为什么不自己回答”,我没有答案,因此提出了问题。而且这也不是“扯皮”,FluentInterface 不等于 Method Chaining 似乎已经很清楚了。
          • @Pacerier 好吧,这没有任何意义。您声称很明显它们是不同的,但您自己却没有答案?我相信你在拖钓。先生,您好。
          • Cool down,我不明白为什么代表数量与跃升至negativity 的趋势相关。如果你也没有答案也没关系,这没什么大不了的。如果慢慢阅读,我的上述评论并非“毫无意义”:从 Fowler 的文章中清楚表明,方法链和 FluentInterface 之间存在区别,即使区别本身不清楚.虽然我试图通过您的回复来提高这里的答案质量,但您始终可以保持原样。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-02-17
          • 2014-12-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-02-19
          • 1970-01-01
          相关资源
          最近更新 更多