Fluent interfaces and leaky abstractions

前端 未结 8 2227
攒了一身酷
攒了一身酷 2021-02-13 14:30

What is a fluent interface? I can\'t find a good definition of this, but all I get are long code examples in a language I am not very familiar with (e.g. C++).

Also, wha

相关标签:
8条回答
  • 2021-02-13 15:01

    A fluent interface is an API that allows you to write code that reads more or less like normal English. For example:

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

    Method-chaining is usually used as part of the implementation, but there is more to it than that. To quote Fowler:

    I've also noticed a common misconception - many people seem to equate fluent interfaces with Method Chaining. Certainly chaining is a common technique to use with fluent interfaces, but true fluency is much more than that.

    It is also often called an internal DSL, since the syntax resembles that of a DSL, but it is implemented inside the host language instead of being processed by a parser.

    0 讨论(0)
  • 2021-02-13 15:04

    Neal Ford does a nice job of explaining and giving Fluent Interface examples in his book the 'Productive Programmer'.

    Traditional Object or 'bean' with getters/setters:

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

    Meet the same need with a fluent interface:

    Car car = Car.describedAs()
      .box()
      .length(50.5)
      .type(Type.INSULATED)
      .includes(Equipment.LADDER)
      .lining(Lining.CORK);
    
    0 讨论(0)
  • 2021-02-13 15:06

    In a fluent interface, a object's methods will return a reference to the object, so that you can chain the method calls together.

    For example, in NValidate, I did this to simplify parameter validation:

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

    I can't speak to leaky abstractions, though.

    0 讨论(0)
  • 2021-02-13 15:11

    Here's a regular every-day interface:

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

    And here's a fluent interface:

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

    The most obvious difference is that when we return a void, we return instead an instance of the interface type. What's understood is that the interface returned is the CURRENT INSTANCE, not a new instance of the same type. Of course, this isn't enforceable, and in the case of immutable objects (like string) it is a different instance but can be considered to be the same instance only updated.

    Here are examples of their use:

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

    Notice that the fluent interface is easier to use when chaining different calls. IRL, check out the Linq extension methods and how each call is designed to flow into another. None of the methods return void, even if it would be a valid result.

    0 讨论(0)
  • A fluent interface a term Eric Evans coined and it's just another name for method chaining. Martin Fowler wrote a couple of articles on this subject, but it roughly looks like this:

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

    Fluent interface are generally used to create named parameters in a language that doesn't support them (the Named Parameter Idiom in C++ for example), or in Domain Specific Languages to make the code read more fluently.

    I've seen them being used for everything from image processing libraries, to regular expression libraries, 3D libraries. Other examples include the construction of tree structures, lists, or other datastructures. Everything that requires the construction of complex objects (load of parameters) can make use of Fluent Interfaces to make it more readable. For example, compare the previous example to the CreateWindow function call:

     ::CreateWindow(
          "Window class", 
          "Window title", 
          dwStyle, X, Y, 
          nWidth, nHeight, 
          hWndPant, hMenu, 
          hInstance, NULL
     );
    
    0 讨论(0)
  • 2021-02-13 15:19

    Thanks guys.

    Great description.

    My thought about fluent interfaces where that they were for readability. I could always read a chain of methods and how one is related to the previous/next method.

    E.g. like the poster who posted the validation example (I have written code similar to that before).

    0 讨论(0)
提交回复
热议问题