Meeting 6

Aus Java Student User Group Austria - Java + JVM in Wien Österreich / Vienna Austria
Wechseln zu: Navigation, Suche

When: Monday, November 3rd, 2008 - 19:00

Where: Freihaus HS4


Bazaar

Held by Florian M.

Download the slides (pdf, 68KB).

Fluent Interfaces

Unfortunately there are no slides available for the presentation from Axel G. But you can have a look at Martin Fowler's article about fluent interfaces who kind of coined the term.

To be short, it's kind of an internal DSL with the aim to provide a readable API.


The following example shows the use of JMock: <source lang="java"> mock.expects(once()).method("m").with(or(stringContains("hello"),

                                        stringContains("howdy")));

</source>


In a preceding presentation about db4o we had similar code for querying a OO database: <source lang="java"> query.descend("matrNr").constrain(500000).greater().equal(); </source>


Simple Example

Personally, I (Christoph Pickl) like to use that technique to construct complex objects which should be on the one side immutable but on the other easy to create (values for creation are sometimes scattered throughout many different classes). Imagine you have a Person type with its only attributes name:String and age:int which are both marked final.

<source lang="java"> public class Person {

 // final attributes
 private final String name;
 private final int age;
 // constructor is package-private, so only PersonBuilder can access it
 Person(final String name, final int age) {
   this.name = name;
   this.age = age;
 }
 // simple getter methods
 public String getName() {
   return this.name;
 }
 public int getAge() {
   return this.age;
 }

} </source>


A PersonBuilder now provides the single possibility to create Persons. The interesting thing here is, that it forces the caller to invoke methods for each attribute of Person, and returns itself but with another "face" (builder interface). Only if the last attribute was set the create() method is accessible which finally returns a new instance. Guard clauses (actually used to avoid nested conditions) help to ensure that the instance will only store valid values. This gives the advantage of defining a valid state outside of the entity itself so it can be reused in a complete different environment with complete different limitations (name can be null, etc). <source lang="java"> public class PersonBuilder implements IPersonBuilder1, IPersonBuilder2, IPersonBuilderFinisher {

 // (non-final) attributes
 private String name;
 private int age;
 // constructor
 private PersonBuilder() {
   // constructor not visible; use static factory method build()
 }
 public static IPersonBuilder1 build() {
   return new PersonBuilder();
 }
 // fluent interface implementations
 public IPersonBuilder2 name(final String name) {
   if(name == null) { // guard clause
     throw new IllegalArgumentException("name == null");
   }
   this.name = name;
   return this;
 }
 public IPersonBuilderFinisher age(final int age) {
   if(age < 0 || age > 200) { // guard clause
     throw new IllegalArgumentException("age not within range 0-200: " + age);
   }
   this.age = age;
   return this;
 }
 public Person newInstance() {
   return new Person(this.name, this.age);
 }

}

</source>


Two obvious disadvantages come to my mind: The overhead of creating an interface for each method plus one for the creation and you have to invoke the methods in exactly the way they are defined via the fluent interfaces. You can't change the order, and you can't skip any attribute. <source lang="java"> public interface IPersonBuilder1 {

 IPersonBuilder2 name(String name);

} public interface IPersonBuilder2 {

 IPersonBuilderFinisher age(int age );

} public interface IPersonBuilderFinisher {

 Person newInstance();

} </source>


Finally you can use the builder to create Person instances via a very readable API. The second Person instance is created in a distributed manner, which would not be that easy if you would have to create the instance in the old-fashioned way because of final attributes you have to pass to the constructor. <source lang="java"> public class App {

 public static void main(final String[] args) {
   // short version:
   final Person p1 = PersonBuilder.build().name("Christoph Pickl").age(23).newInstance();
   // or to use different builders and pass them around
   final IPersonBuilder2 builder2 = PersonBuilder.build().name("Christopherus Pickl");
   final Person p2 = App.computeAge(builder2).newInstance();
 }
 private static IPersonBuilderFinisher computeAge(final IPersonBuilder2 builder2) {
   final int age = 42; // some complicated computation
   return builder2.age(age);
 }

} </source>