Monday, June 21, 2010

Fluent Interfaces...Great.when.good && Fn.complicated.ifelse().true.

While scanning the internet, looking for something to comment on and maybe learn, My wife, a .Net Developer, mentioned...

Sharp Test Ex... I went to check it out and was instantly intrigued...
With syntax shown below who can blame me, nice readable tests:

const string something = "something";
something.Should().Contain("some");
something.Should().Not.Contain("also");
something.ToUpperInvariant().Should().Not.Contain("some");

var ints = new[] { 1, 2, 3 };
ints.Should().Have.SameSequenceAs(new[] { 1, 2, 3 });
ints.Should().Not.Have.SameSequenceAs(new[] { 3, 2, 1 });
ints.Should().Not.Be.Null();
ints.Should().Not.Be.Empty();


I then remembered a couple years back I came across something Martin Fowler chatted about: Fluent Interface

I never paid too much attention to it then, but as the years add up and I get more lazy, this appeals to me more. So not to be out done by .Net I went looking for java equivalents. I found 2, FEST and OP4j.

FEST has a couple sub projects: Reflection, Swing, JavaFX, Mock, Assertions
To compare with the .Net example above I'll go to FEST Asserts.
FEST Asserts
"
Provides a fluent interface for writing assertions. Its main goal is to improve test code readability and make maintenance of tests easier.
"

With example Syntax below... nice ... simple ... readable... great, I'll be implementing that in the next project I have a little bit of spare time. It doesn't cater for everything but from the looks of it there will 0 ramp up time and should be usable / understandable right after adding it to the maven pom and downloading the libs.

int removed = employees.removeFired();
  assertThat(removed).isZero();

  List< Employee > newEmployees = employees.hired(TODAY);
  assertThat(newEmployees).hasSize(6)
                          .contains(frodo, sam);

  String[] newHires = employees.newHiresNames();
  assertThat(newHires).containsOnly("Gandalf", "Arwen", "Gimli"); 

  assertThat(yoda).isInstanceOf(Jedi.class)
                  .isEqualTo(foundJedi)
                  .isNotEqualTo(foundSith);



Now OP4J...
op4j

"
op4j is a powerful implementation of the Fluent Interface style of code. It allows you to create chained expressions which apply both predefined or user-defined functions to your objects in a fluid and readable way. This improves the way your code looks and greatly reduces the complexity of executing auxiliary low-level tasks in the highly bureaucratic, statically -and strongly- typed language that Java is.
"

"several hundred functions with more than one thousand different parameterizations"

Off the bat seems like a monster. They seem to have put a lot of time and effort into "bending the java spoon". From a quick look it seems they cater for a ton of common code occurences and allow you to condense quite a bit of code into 1 liners.
Although I do see value in this framework, I have some concerns on how much there is to learn, the ramp up time required, and how easy it would be for a junior developer to maintain.


Example syntax below:

// Without op4j
  Set< String > set = new LinkedHashSet< String >(list);      
  // With op4j
  Set< String > set = Op.on(list).toSet().get();


 Set < Calendar > set = Op.on(list).toSet().removeAllNull().forEach()
.exec(FnString.toCalendar("dd/MM/yyyy")).get();

 Function< List < String >,Set < Calendar > > dateProcessFunction = 
 Fn.onListOf(Types.STRING)
.toSet().map(FnString.toCalendar("dd/MM/yyyy"))
.removeAllNullOrTrue(FnCalendar.after(now)).get();



In conclusion, it is not entirely fair to compare OP4J with the much smaller "testing" fluent interfaces as they are targeted at different functionality. However, I think the idea of Fluent Interfaces is quite a nice one. On large systems with internal frameworks and structured code environments like large corporates and banks, defining and implementing a proprietary fluent interface type of framework could , if done correctly, be very very useful and simplify large amounts of commonly used code. There is however, a distinct danger that it may also lead to vastly harder to read code that is also harder to maintain and debug instead of the original concept of "The API is primarily designed to be readable and to flow"

What I do like of both "FEST Asserts" and the .Net "Sharp Test Ex" is that they are limited to testing and geared only towards quicker, more understandable test cases. Anything that makes testing quicker, easier or a little more fun is well worth it.


I also stumbled across the following,

Fluent Build eclipse builder.

How not yet had time to investigate it properly, but definitely worth keeping an eye on.

2 comments:

  1. A domain specific fluent interface - something like findCode(blah).primary() or findPrimaryCode(cows)?

    Too bad it could never work in quickRules...

    ReplyDelete
  2. Not that simple actually, will have to give it some thought but I would guess it would fit better with some very specific groups of functionality like in our world coding: Codecontainer.findCode("XX11").isActive(date).isPrimary().isICD()

    but to get the above line right would take a little bit of magic I thinks.

    on a side note I implemented FEST asserts today in a project... awesome... now I just have to decided if I am going to bother with the red tape of getting it into our repo.

    ReplyDelete

Popular Posts

Followers