问题
I discovered yesterday that I can simulate a fluent interface if I return the class instance from each method like this...
public class IsThisFluent
{
public IsThisFluent Stuff()
{
//...
return this;
}
public IsThisFluent OtherStuff()
{
// ...
return this;
}
}
Is this all there is to it?
I admit, I'm a bear of very little brain and I want to carry on this this but I thought it might be best to check with a grown up.
Am I missing something?
Is there a 'gotcha' that I haven't spotted with this pattern?
回答1:
return this
is not all there is to fluent interfaces.
Chaining methods is a simplistic form of building a fluent API, but fluent APIs generally look like DSLs (domain specific languages) and are much, much harder to design.
Take Moq as an example:
new Mock<IInterface>()
.Setup(x => x.Method())
.CallBack<IInterface>(Console.WriteLine)
.Returns(someValue);
The
Setup
method, defined on the typeMock<T>
, returns an instance ofISetup<T, TResult>
.The
Callback
method, defined forICallback<TMock, TResult>
returns an instance ofIReturnsThrows<TMock,TResult>
. Note thatISetup<T, TResult>
extendsIReturnsThrows<TMock,TResult>
.Finally,
Returns
is defined onIReturns<TMock,TResult>
and returnsIReturnsResult<TMock>
. Also note thatIReturnsThrows<TMock,TResult>
extendsIReturnsResult<TMock>
.
All these little nuances are there to force you to call these methods in a particular order, and to forbid you from calling Setup
twice in a row, for example. Or from calling Returns
before you call Setup
.
These details are very important to ensure a good user experience.
To read more on designing fluent interfaces, take a look at Martin Fowler's article on FluentInterface. FluentAssertions is another prime example of how complex the design might get - but also of how much more readable the outcome is.
回答2:
That's pretty much it.
Here's a really good article on it:
http://rrpblog.azurewebsites.net/?p=33
EDIT
The original site seems to have died, so here's WayBackMachine to the rescue
I also really like this example from this answer: https://stackoverflow.com/a/1795027/131809
public class Coffee
{
private bool _cream;
public Coffee Make { get new Coffee(); }
public Coffee WithCream()
{
_cream = true;
return this;
}
public Coffee WithOuncesToServe(int ounces)
{
_ounces = ounces;
return this;
}
}
var myMorningCoffee = Coffee.Make.WithCream().WithOuncesToServe(16);
Which reminds me, I need a coffee now.
回答3:
Nope, that's pretty much it.
The idea behind is that you can chain method calls together manipulating internal state as you go. Ultimately the main goal of a Fluent interface is readability, LINQ being a very good example of one.
来源:https://stackoverflow.com/questions/29694576/making-my-class-fluent