Recompilation Time and Layering

When following an iterative development methodology compilation time is a concern. In order to safely make a rapid series of small transformations to a program you need to regularly recompile and test the changes you are making. Using an IDE which can do incremental compilation is very helpful, but more complex programs with code and resource generation phases, when using languages without that kind of compilation support, and when verifying that the CI build will work, still require a fast full automated build and test cycle.

In a previous article I discussed a design principal that can be used to try to minimise the amount of time required to do a full compile of all the code in an application.

It is also possible to select design principals which will reduce the amount of recompilation required by any given code change. Conveniently it turns out that same abstract layering approach can be used here too.

We return to the example simple application;

application depending on library 1 depending on library 2 depending on library 3 depending on library 4

How long is required to compile if a change is made in each component, if each component takes “m” time to compile?

Component Compilation Order Compilation Time
library4 library4,library3,library2,library1,application 5m
library3 library3,library2,library1,application 4m
library2 library2,library1,application 3m
library1 library1,application 2m
application application 1m

If we calculate the instability of each module taking into account transitive dependencies. Abusing the definitions slightly we get:

instability = (Ce/(Ca+Ce))

Component Ce Ca I
library4 0 4 0
library3 1 3 0.25
library2 2 2 0.5
library1 3 1 0.75
application 4 0 1

Comparing the instability with the compilation time is interesting. Less stable packages should have lower compilation times, which we see here:

Component Time I
library4 5m 0
library3 4m 0.25
library2 3m 0.5
library1 2m 0.75
application 1m 1

Reducing Recompilation Time

We can restructure our application using the dependency inversion principal. We split each layer into an abstract (interface) layer and a concrete implementation layer.

The application module becomes responsible for selecting the concrete implementation of each layer and configuring the layers of the application. This design pattern is known as Inversion of Control.

application depends on libraries, but libraries depend on abstract layer interfaces

Taking into account the ability of a multi-core machine to compile modules in parallel, we get the following minimum compilation times:

Component Changed Compilation Order Compilation Time
library4 library4,application 2m
<<library4>> <<library4>>,{library4,library3},application 3m
library3 library3,application 2m
<<library3>> <<library3>>,{library3,library2},application 3m
library2 library2,application 2m
<<library2>> <<library2>>,{library2,library1},application 3m
library1 library1,application 2m
application application 1m

The compilation time for the abstract modules is uniformly 3m, and the compilation time for the concrete modules is uniformly 2m. The application itself is always the last to be compiled so is 1m as before.

How has the design change affected the stability of the modules?

Component Ce Ca I
library4 1 1 0.5
<<library4>> 0 3 0
library3 2 1 0.66
<<library3>> 0 3 0
library2 2 1 0.66
<<library2>> 0 3 0
library1 1 1 0.5
application 4 0 1

Again we see that the less stable a module is the lower its compilation time:

Component Time I
library4 2m 0.5
<<library4>> 4m 0
library3 2m 0.66
<<library3>> 4m 0
library2 2m 0.66
<<library2>> 4m 0
library1 2m 0.5
application 1m 1

This example is quite simple, so we see the instability metric of the modules being dominated by the application. It does illustrate however how we can use dependency inversion to help establish an upper bound on the recompilation time for any given change in a concrete module. I will examine a more realistic example in a future article.

Compilation Time and Layering

On a modern computer with multiple cores the total compile time of an application is related to the longest path though the dependency graph of the application.

In a statically type checked language that supports separate compilation of application components (into, for example jars or assemblies), it is generally necessary to compile the dependencies of an application or library before compiling the application or library itself.

A typical layered application might look like this:

application depending on library 1 depending on library 2 depending on library 3 depending on library 4

The compile order for such an application would be library4 then library3 then library2 then library1 then application. The longest path through the dependency graph is 5.

Assuming, for simplicity, that the compile time for each module is approximately the same (m) the total compile time for the application (t) is 5m.

t = 5m

Taking Advantage of Parallel Compilation

We can restructure our application using the dependency inversion principal. We split each layer into an abstract (interface) layer and a concrete implementation layer.

The application module becomes responsible for selecting the concrete implementation of each layer and configuring the layers of the application. This design pattern is known as Inversion of Control.

application depends on libraries, but libraries depend on abstract layer interfaces

The compile order for such an application is much more flexible than that of the earlier design allowing some modules to be compiled in parallel.

The longest path through the dependency graph is 3.

Assuming again, that the compile time for each module is approximately the same (m) the minimum compile time for the application (t) is 3m.

t = 3m

Cloudbees Open Source Software infrastructure

Cloudbees offers free Jenkins hosting in the cloud to FOSS projects.

I have been using cloudbees for the continuous integration build of fluent-reflection for about a month. It has been pretty predictable and reliable so far, and offers enough of the Jenkins features to be useful. Jenkins itself is very easy to configure with a maven project.

It was quite easy to use it in conjunction with GitHub.

Screenshot of the fluent reflection jenkins build on cloudbees

Dependency Inversion

What is a Dependency

For the purposes of this discussion a class A has a dependency on another class B, iff you cannot compile class A without class B.

Example

class A {B b;}
class B { }

Box A with an arrow pointing to box B

We have to compile class B before (or at the same time as) class A.

Other kinds of Dependency

Anywhere the name “B” appears in class A creates a dependency. Some other examples of dependencies are:

class A extends B { }
class A implements B { }
class A { void method(B b) { } }

Transitive Dependencies

If a class A depends on another class B which itself has dependencies then the dependencies of class B are effectively dependencies of class A

Example:

class A { B b; }
class B { C c; } 
class C { }

Box A with an arrow to Box B with an Arrow to Box C, dotted arrow from Box A to Box C

We have to compile class C before (or at the same time as) class B and class A.

The Problem

When class C changes, we have to recompile and retest both class A and class B. In a large system this can take a very long time. It also means that you have to know about class C in advance; you cannot decide on class C after deciding on class A.

If class C is a more concrete class then it might change more frequently than class A. This will cause class A to be recompiled/tested much more frequently than it otherwise would need to be.

The Solution

Inverting Dependencies

Have class A and class B both depend on an abstraction I. This inverts the direction of the dependency arrow on class B.

interface I { }
class A { I i; }
class B implements I { }

Box A with arrow to Box I, Box B with arrow to Box I

Breaks Transitive Dependency

The really helpful effect of this inversion is that it also breaks the transitive dependency from class A onto class B

interface I{ }
class A { I i; }
class B implements I{ C c; }
class C { }

Box A arrow to Box I, Box B arrow to Box I, Box B arrow to box C. Box A dotted arrow to Box C deleted

Dependency Inversion Principle

This is an application of the Dependency Inversion Principle:

  • High level modules should not depend on low level modules. Both should depend on abstractions.
  • Abstractions should not depend upon details. Details should depend upon abstractions.

Compositional Patterns for Test Driven Development

This article is going to look at how to implement a parameterizable algorithm so it can both conform generally to the open/closed principal and also most effectively be tested.

A common pattern for code reuse, implementation selection, or extension, is to use class inheritance and the template method pattern. This is an example of an implementation of the template method pattern with two different variations B and C:

abstract class A {
    public void doSomething() {
        doP();
        doQ();
        doR();
    }
 
    protected abstract void doP();
    protected abstract void doQ();
    protected abstract void doR();
}
 
class B extends A {
    @Override protected void doP() { /* do P the B way */}
    @Override protected void doQ() { /* do Q the B way */}
    @Override protected void doR() { /* do R the B way */}
}
 
class C extends A {
    @Override protected void doP() { /* do P the C way */}
    @Override protected void doQ() { /* do Q the C way */}
    @Override protected void doR() { /* do R the C way */}
}

Refactoring template method pattern to Strategy Pattern

We can always convert this template method pattern to a compositional pattern by performing a refactoring in the following steps (if you have member variable access there are a couple more steps, but I’ll cover them in a follow up article):

Step 1; encapsulate the construction of the B and C strategies:

abstract class A {
    public void doSomething() {
        doP();
        doQ();
        doR();
    }
 
    protected abstract void doP();
    protected abstract void doQ();
    protected abstract void doR();
}
 
class B extends A {
    public static A createB() {
        return new B();
    }
 
    @Override protected void doP() { /* do P the B way */}
    @Override protected void doQ() { /* do Q the B way */}
    @Override protected void doR() { /* do R the B way */}
}
 
class C extends A {
    public static A createC() {
        return new C();
    }
 
    @Override protected void doP() { /* do P the C way */}
    @Override protected void doQ() { /* do Q the C way */}
    @Override protected void doR() { /* do R the C way */}
}

Step 2; extract an interface for the strategy methods:

interface S {
    void doP();
    void doQ();
    void doR();
}
 
abstract class A implements S {
    private final S s = this;
 
    public void doSomething() {
        s.doP();
        s.doQ();
        s.doR();
    }
}
 
class B extends A {
    public static A createB() {
        return new B();
    }
 
    @Override public void doP() { /* do P the B way */}
    @Override public void doQ() { /* do Q the B way */}
    @Override public void doR() { /* do R the B way */}
}
 
class C extends A {
    public static A createC() {
        return new C();
    }
 
    @Override public void doP() { /* do P the C way */}
    @Override public void doQ() { /* do Q the C way */}
    @Override public void doR() { /* do R the C way */}
}

Step 3; pass the strategies into the superclass instead of using this:

interface S {
    void doP();
    void doQ();
    void doR();
}
 
final class A {
    private final S s;
 
    public A(final S s) {
        this.s = s;
    }
 
    public void doSomething() {
        s.doP();
        s.doQ();
        s.doR();
    }
}
 
class B implements S {
    public static A createB() {
        return new A(new B());
    }
 
    public void doP() { /* do P the B way */}
    public void doQ() { /* do Q the B way */}
    public void doR() { /* do R the B way */}
}
 
class C implements S {
    public static A createC() {
        return new A(new C());
    }
 
    public void doP() { /* do P the C way */}
    public void doQ() { /* do Q the C way */}
    public void doR() { /* do R the C way */}
}

Advantage of the compositional style

Less fragile

Changes to A such as new methods are much less likely to break the strategies in the compositional style.

Easier to Test

In the compositional style, class A can be tested by itself using Mocks. As can class B and class C. In the inheritance style class B and class C cannot be tested without also testing class A. This leads to duplication in the tests, as features of class A are re-tested for every subclass.

Easier to Reuse

In the compositional style class B and class C can be reused in other contexts where A is not relevant. In the inheritance style this type of reuse is not possible.

Emergent Model/Domain concepts

If class B or class C are reused in a different context, it may turn out that during subsequent refactoring they develop a new role within the system. I may even discover an important new domain concept.

Introducing Fluent Reflection

I am in the processes of releasing the first alpha build of fluent-reflection, you can get the snapshot version from the fluent-reflection snapshot repository.

Motivation

Reflection code is difficult to write in Java, and even harder to read once it has been written. Although the Java Reflection API allows complete access to the Java runtime type system, the complexity of using the Java Reflection API reduces its usefulness. I don’t want to have to write any more code like this:

final List<Method> getMethods = new ArrayList<Method>();
 
Class<? super Bean> klass = Bean.class;
while (klass != null) {
   final Method[] declaredMethods = klass.getDeclaredMethods();
   for (final Method method : declaredMethods) {
      if (method.getName().startsWith("get") && 
          method.getParameterTypes().length == 0 && 
          !method.getReturnType().equals(void.class)) {
         getMethods.add(method);
      }
   }
   klass = klass.getSuperclass();
   [...]
}

Solution

There are three APIs which have heavily influenced the design of the fluent-reflection API.

  1. LambdaJ
  2. Hamcrest
  3. Jmock 2

All of them are examples of a style of API design known as fluent interfaces. They use a combination of static methods, method chaining, generic methods, static factory methods and object scoping to produce a type safe and readable programming style.

In particular, the combination of method chaining and generic methods can help the user discover the usage of each part of the API and also takes advantage of Java’s static type system to prevent programming errors.

Using fluent-reflection the above code can be re-written in declarative style:

final List<ReflectedMethod> getMethods = 
   object(new Bean()).methods(
      callableHasNameStartingWith("get").
      and(callableHasNoArguments()).
      and(not(callableHasVoidReturn())));

Extensibility

The query methods in the API are given queries described by Hamcrest Matchers. So users can easily construct custom and reusable queries.

For example, this Matcher can select methods with a particular annotation:

class MatcherAnnotatedWith extends ReflectionMatcher<ReflectedAnnotated> {
    private final Class<? extends Annotation> annotation;
 
    public MatcherCallableAnnotatedWith(final Class<? extends Annotation> annotation) {
        this.annotation = annotation;
    }
 
    @Override protected boolean matchesSafely(final ReflectedAnnotated item) {
        return item.annotation(reflectedTypeReflectingOn(annotation)) != null;
    }
 
    @Override public void describeTo(final Description description) {
        description.appendText("callable annotated with ").appendValue(annotation);
    }
}

Mashability

Because the API uses the de facto standard Hamcrest matchers, and the Java collections framework it can be combined with other libraries in powerful ways.

For example, calling all of the @PostConstruct methods on an Object using LambdaJ and fluent-reflection:

forEach(
   object(subject).methods(annotatedWith(PostConstruct.class)),
   ReflectedMethod.class).call();

Fail Fast – Fail During Compile

One of the fastest ways for faulty software to fail is at compile time.

Here is an example of slow failing code, it will fail at runtime if it comes across an implementation of A that it did not expect. This can happen if a developer adds a new implementation of A but forgets to update this code (or is not aware of it, or it is located in some other dependent library).

interface A { }
class B implements A { }
class C implements A { }
 
void doSomething(A a) {
   // not recommend
   if(a instanceof B) {
      doMyBThings((B) a);
   } else if(a instanceof C) {
      doMyCThings((C) a);
   } else {
      throw IllegalArgumentException("unexpected kind of A");
   }
}

One way to move these failures to compile time would be to move the responsibility for doing my B things and doing my C things on to the classes B and C respectively.

interface A { void doMyThings(); }
class B implements A { void doMyThings(){ /* do my B things */ } }
class C implements A { void doMyThings(){ /* do my C things */ } }
 
void doSomething(A a) {
   a.doMyThings();
}

If I add a new type D the compiler forces us to implement doMyThings()

class D implements A { void doMyThings(){ /* do my D things */ } }

Sometimes it is not appropriate to put the responsibility to doMyThings() onto A. It might create undesirable coupling (a dependency) or have some other undesirable property. I can maintain the fail fast property in in other ways.

interface A { void doThings(Things things); }
class B implements A { void doThings(Things things){ things.doBThings(this); } }
class C implements A { void doThings(Things things){ things.doCThings(this); } }
 
interface Things {
      void doBThings(B b);
      void doCThings(C c);
}
 
void doSomething(A a) {
   a.doThings(new Things(){
      void doBThings(B b){ /* do my B things */}
      void doCThings(C c){ /* do my C things */}
   });
}

Adding a new type D, the compiler forces me to implement doThings on D, which leads me to add a new method onto the Things interface, which forces any implementers of Things to handle D types.

class D implements A { void doThings(Things things){ things.doDThings(this); } }
 
interface Things {
      void doBThings(B b);
      void doCThings(C c);
      void doDThings(D d);
}
 
void doSomething(A a) {
   a.doThings(new Things(){
      void doBThings(B b){ /* do my B things */}
      void doCThings(C c){ /* do my C things */}
      void doDThings(D d){ /* do my D things */}
   });
}

However, a user might not want to handle every new implementation of A, I can provide default implementations of the methods:

class BaseThings implements Things {
      void doBThings(B b) { };
      void doCThings(C c) { };
}
 
void doSomething(A a) {
   a.doThings(new BaseThings(){
      void doBThings(B b){ /* do my B things */}
      void doCThings(C c){ /* do my C things */}
   });
}

When I add D I also add a default implementation, so I do not have to add any new handling code:

class BaseThings implements Things {
      void doBThings(B b) { };
      void doCThings(C c) { };
      void doDThings(D d) { };
}
 
void doSomething(A a) {
   a.doThings(new BaseThings(){
      void doBThings(B b){ /* do my B things */}
      void doCThings(C c){ /* do my C things */}
   });
}

I can choose to either have my code break when new implementations of A are added (by extending Things) or not to break (by extending BaseThings).