Fluent Interfaces - Method Chaining

你。 提交于 2019-11-27 02:58:28

You can create a fluent interface in any version of .NET or any other language that is Object Oriented. All you need to do is create an object whose methods always return the object itself.

For example in C#:

public class JohnBuilder
{
    public JohnBuilder AddSmartCode(string s)
    {
        // do something
        return this;
    }

    public JohnBuilder WithfluentInterface(string s)
    {
        // do something
        return this;
    }

    public JohnBuilder ButHow(string s)
    {
        // do something
        return this;
    }
}

Usage:

John = new JohnBuilder()
    .AddSmartCode("c#")
    .WithfluentInterface("Please")
    .ButHow("Dunno");

The core idea behind building a fluent interface is one of readability - someone reading the code should be able to understand what is being achieved without having to dig into the implementation to clarify details.

In modern OO languages such as C#, VB.NET and Java, method chaining is one way that this is achieved, but it's not the only technique - two others are factory classes and named parameters.

Note also that these techniques are not mutually exclusive - the goal is to maximize readabilty of the code, not purity of approach.

Method Chaining

The key insight behind method chaining is to never have a method that returns void, but to always return some object, or more often, some interface, that allows for further calls to be made.

You don't need to necessarily return the same object on which the method was called - that is, you don't always need to "return this;".

One useful design technique is to create an inner class - I always suffix these with "Expression" - that exposes the fluent API, allowing for configuration of another class.

This has two advantages - it keeps the fluent API in one place, isolated from the main functionality of the class, and (because it's an inner class) it can tinker with the innards of the main class in ways that other classes cannot.

You may want to use a series of interfaces, to control which methods are available to the developer at a given point in time.

Factory Classes

Sometimes you want to build up a series of related objects - examples include the NHibernate Criteria API, Rhino.Mocks expectation constraints and NUnit 2.4's new syntax.

In both of these cases, you have the actual objects you are storing, but to make them easier to create there are factory classes providing static methods to manufacture the instances you require.

For example, in NUnit 2.4 you can write:

Assert.That( result, Is.EqualTo(4));

The "Is" class is a static class full of factory methods that create constraints for evaluation by NUnit.

In fact, to allow for rounding errors and other imprecision of floating point numbers, you can specify a precision for the test:

Assert.That( result, Is.EqualTo(4.0).Within(0.01));

(Advance apologies - my syntax may be off.)

Named Parameters

In languages that support them (including Smalltalk, and C# 4.0) named parameters provide a way to include additional "syntax" in a method call, improving readability.

Consider a hypothetical Save() method that takes a file name, and permissions to apply to the file after saving:

myDocument.Save("sampleFile.txt", FilePermissions.ReadOnly);

with named parameters, this method could look like this:

myDocument.Save(file:"SampleFile.txt", permissions:FilePermissions.ReadOnly);

or, more fluently:

myDocument.Save(toFile:"SampleFile.txt", withPermissions:FilePermissions.ReadOnly);

AFAIK, the term fluent interface does not specify a specific technology or framework, but rather a design pattern. Wikipedia does have an extensive example of fluent interfaces in C♯.

In a simple setter method, you do not return void but this. That way, you can chain all of the statements on that object which behave like that. Here is a quick example based on your original question:

public class JohnBuilder
{
    private IList<string> languages = new List<string>();
    private IList<string> fluentInterfaces = new List<string>();
    private string butHow = string.Empty;

    public JohnBuilder AddSmartCode(string language)
    {
        this.languages.Add(language);
        return this;
    }

    public JohnBuilder WithFluentInterface(string fluentInterface)
    {
        this.fluentInterfaces.Add(fluentInterface);
        return this;
    }

    public JohnBuilder ButHow(string butHow)
    {
        this.butHow = butHow;
        return this;
    }
}

public static class MyProgram
{
    public static void Main(string[] args)
    {
        JohnBuilder johnBuilder = new JohnBuilder().AddSmartCode("c#").WithFluentInterface("Please").ButHow("Dunno");
    }
}
Andre Vianna

Sometime ago I had the same doubts you are having now. I've done some research and now I'm writing a series of blog posts about techinics of designing a fluent interface.

Check it out at:

Guidelines to Fluent Interface design in C# part 1

I have a section there about Chaining X Nesting that can be interesting to you.

In the following posts I will talk about it in a deeper way.

Best regards,

André Vianna

Fluent interface is achieved in object oriented programming by always returning from your methods the same interface that contains the method. Consequently you can achieve this effect in java, javascript and your other favorite object oriented languages, regardless of version.

I have found this technique easiest to accomplish through the use of interfaces:

public interface IFoo
{
    IFoo SetBar(string s);
    IFoo DoStuff();
    IFoo SetColor(Color c);
}

In this way, any concrete class that implements the interface, gets the fluent method chaining capabilities. FWIW.. I wrote the above code in C# 1.1

You will find this technique littered throughout the jQuery API

A couple of things come to mind that are possible in .Net 3.5/C# 3.0:

  1. If an object doesn't implement a fluent interface, you could use Extension Methods to chain your calls.

  2. You might be able to use the object initialization to simulate fluent, but this only works at instantiation time and would only work for single argument methods (where the property is only a setter). This seems hackish to me, but the there it is.

Personally, I don't see anything wrong with using function chaining if you are implementing a builder object. If the builder object has chaining methods, it keeps the object you are creating clean. Just a thought.

This is how I've built my so called fluent interfaces or my only forary into it

Tokenizer<Bid> tkn = new Tokenizer<Bid>();
tkn.Add(Token.LambdaToken<Bid>("<YourFullName>", b => Util.CurrentUser.FullName))
    .Add(Token.LambdaToken<Bid>("<WalkthroughDate>",
          b => b.WalkThroughDate.ToShortDateString()))
    .Add(Token.LambdaToken<Bid>("<ContactFullName>", b => b.Contact.FullName))
    .Cache("Bid")
    .SetPattern(@"<\w+>");

My example required .net 3.5 but that's only cause of my lambda's. As Brad pointed out you can do this in any version of .net. Although I think lambda's make for more interesting possibilities such as this.

======

Some other good examples are nHibernate's Criteria API, there is also a fluent nhibernate extension for configuring nhibernate but I've never used it

Dynamic keyword in C# 4.0 will make it possible to write dynamic style builders. Take a look at following article about JSON object construction.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!