Tuesday, March 4, 2014

Break the bloated library cycle with Java 8

Most of us have, at some point in time, wondered why all successful java libraries tend to "bloat" by adding new stuff all the time. Spring is a great example. Why on earth is there spring-jdbc, spring-transaction or even spring-web ?

Apart from the obvious market-wise need for a companies to appear as a continuous sources of innovation (investors like that), there are some technical issues involved too. This post looks at how java8 can change some of the dependency issues; it may even change how you do java7 right now.

Java7


The dilemma: A class/library wishes to reuse external services, which introduces dependencies.

public class MyClass {
    private ExternalDependency1 ed1;
    private ExternalDependency1 ed2;
   public int doSomething(String input){
       return ed1.calculate( ed2.transform( input));
   }
}

Now this is the classical reason why need to make *our* module depend on something external, ExternalDependency1 and ExternalDependency2 are defined in another project. This is basically the technical reason why we have 280 different "spring" projects; they all tie in with their different dependencies.

Java8 to the rescue

In java8, we can easily move the declaration of the required type *into* MyClass, which makes it self-contained with respect to dependencies:

public class MyClass {

public interface InternalDependency1 {

int calculate(String source);
}
public interface InternalDependency2 {
String transform(String source);
}
private InternalDependency1 id1;
private InternalDependency2 id2;
public MyClass(InternalDependency1 id1, InternalDependency2 id2) {
this.id1 = id1;
this.id2 = id2;
}
public void doSomething(String input){
id1.calculate( id2.transform( input));
}
}

At first glance, this seems like just additionally bloating the code. We need to look at the use before it all becomes clear:

ExternalDependency1 ed1 = new SomeObject1();
ExternalDependency2 ed2 = new SomeObject1();
MyClass mc = new MyClass( ed1::calculate,         ed2::transform);

Now this is where the subtle magic happens; any method that matches the *shape* of the calculate or transform method will be autoboxed into something that can be *used* as InternalDependency1/2.

With this one magic little change, MyClass suddenly lost all its external dependencies.

What about java.util.function.Function ?


Some of you will notice that I am using 1 method interfaces instead of the generic java.util.function.Function class. There are several reasons for this:

A) It is an explicit type that will some day provide IDE support. Current IDE support is somewhat sketchy, but this will improve.
B) "MyClass" is a library component that is expected to have clients; the "cost" of these interfaces making the external contract of "MyClass" 100% clear is probably worth it in the long run.
C) This is java5/6/7 compatible, although the java7 client will have a slightly less cool client api (anonymous inner class galore)
D) If you use Function, the only thing expressing the intention of the function will be the local variable name; in this case id1 and id2. This makes our java code all javascripty; much as I love javascript I am a little sceptical about too this in java; at least for "library" style code.




No comments:

Post a Comment