Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
598 views
in Technique[技术] by (71.8m points)

language agnostic - Fluent Interfaces - Method Chaining

Method chaining is the only way I know to build fluent interfaces.

Here's an example in C#:

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

Assert.IsNotNull(john);

  [Test]
    public void Should_Assign_Due_Date_With_7DayTermsVia_Invoice_Builder()
    {
        DateTime now = DateTime.Now;

        IInvoice invoice = new InvoiceBuilder()
            .IssuedOn(now)
            .WithInvoiceNumber(40)
            .WithPaymentTerms(PaymentTerms.SevenDays)
            .Generate();

        Assert.IsTrue(invoice.DateDue == now.AddDays(7));
    }

So how do others create fluent interfaces. How do you create it? What language/platform/technology is needed?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share
...