Reading Hanselman’s latest, still trying to work out whether I actually like fluent interfaces. The MBUnit example is particularly ugly, to my eyes:
testObject.ShouldBeTheSameObjectAs(targetObject).And.ShouldBeEqualTo(testObject).And.ShouldSatisfy(x => x is Object);
Recently I've been noodling about with writing a unit testing library in JavaScript (what's wrong with jsUnit? Nothing, obviously. Doesn't stop me noodling about.). I ended up with some experimental syntax like this:
// function to test
function add(a, b)
{
return a + b;
}
// tests
with (add)
{
ResultOf(0, 0).Is(0, '0 + 0 = 0');
ResultOf(2, 2).Is(4, '2 + 2 = 4');
ResultOf(-3, 4).Is(1, '-3 + 4 = 1');
}
Which is quite nice, but then I had to go and spoil it with some hideously tortuous hacking to support testing for JavaScript prototype/constructor-style OO:
// class Person to test
function Person(name, age)
{
this.Name = name;
this.Age = age;
}
Person.prototype.Say = function(message)
{
return this.Name + " says '" + message + "'";
}
// tests
with (Person)
{
AfterConstructing("Bob", 21)
.Property("Name").Is("Bob", "set name to Bob")
.Property("Age").Is(21, "set age to 21")
.Calling("Say").With("Hi!").Returns("Bob says 'Hi!'", "Bob has a say method");
// or, alternatively:
with (Instance("Marjorie", 30))
{
Property("Name").Is("Marjorie ", "set name to Marjorie ");
Property("Age").Is(30, "set age to 30");
with (Say)
{
ResultOf("Hi!").Is("Marjorie says 'Hi!'", "Marjorie has a say method");
}
}
}
So I did all that, and now I can’t decide if the fluent interface stuff is actually a good idea or not. I’m particularly not fond of
.Calling("Say").With("Hi!").Returns("Bob says 'Hi!'", "Bob has a say method");
I know with my well-attested love for jQuery I should be all about the method-chaining, too, but I actually think I prefer
with (foo)
{
X();
Y();
Z();
}
to
foo
.X()
.Y()
.Z();
too...
Am I just getting too old for fluent interfaces?
I think the point is that API design is all about making your functionality available in ways that developers can access using comfortable syntax. If a fluent approach makes sense, then go for it, but it doesn't help to shoehorn fluent approaches onto an API at the expense of enabling other syntaxes.
When I finally get round to completing my work on Flogo, I'll also have something to say about how method-chaining APIs let you do neat stuff in C# 3.0...