Embrancing change in APIs with the NetBeans Lookup Library

In my previous entries, I explained why the NetBeans Lookup Library is so cool, and gave examples on how to lookup stuff and on how to create your own travel bags.

It's time now to understand the real power of the library through some use cases, that I'll be showing in this entry and in future ones.

Intent

Build an API that can be easily extended in the future, reducing compilation and refactoring problems.

Motivation

Say you're creating an API for dealing with vegetables, so you create an interface like this:

    public interface Vegetable
    {
      public Color getColor();
      public boolean isHealthy();
    }

That's a very cool Vegetable interface, but later on you start thinking of adding some other properties to your vegetable objects, such as, say, the type of vitamines it provides.

Now, you're in trouble: you need to modify your API to add a method to return your vitamines, and you need to change all classes implementing the Vegetable interface!!

What you can do is to add a travel bag to your veggies, like this:

    public interface Vegetable
    {
      public Color getColor();
      public boolean isHealthy();
      public Lookup getLookup();
    }

And then if you need to add a vitamine you just add it to the lookup in the implementations, without changing the interface at all, like this:

    public class Carrot
      implements Vegetable
    {
      public Lookup getLookup()
      {
        return Lookups.fixed( Vitamine.A, Vitamine.B1 );
      }
    }

And you don't need to change the interface at all!! You can now add vitamines, average weight, and even a price for them easily! Without changing the API, like this:

    public class Carrot
      implements Vegetable
    {
      public Lookup getLookup()
      {
        return Lookups.fixed( Vitamine.A, Vitamine.B1 ,
          new BigDecimal("0.50") );
      }
    }

Users of carrots (i.e., rabbits) can determine the vitamines of the vegetable by searching in its lookup, like this:

  Vegetable v = ...
  Collection<? extends Vitamine> vitamines = 
    carrot.getLookup().lookupAll( Vitamine.class );

Now isn't this cool? The Lookup Library helps avoid the "Incredible Transmogrifying API problem", allowing you to build APIs that embrace change.

Applicability

Use the Lookup Library to build APIs whose object's properties are not well known at design time. By using this technique you can create objects whose properties can be modified easily, with little refactoring.

Structure

Participants

  • AbstractAPIClass, contains a "Lookup" read-only property.
  • ConcreteClass, returns a "Lookup" (a travel bag) with all required properties. You can add or remove properties to "ConcreteClass" without compiling "AbstractAPIClass".
  • Client clients of the API just retrieve the "Lookup" in an abstract class, and look for specific properties in that lookup.

Consequences

As a positive consequence you build APIs whose SPIs can add properties at will, without changing the API.

Abuse of this technique may lead to APIs that are too difficult to understand, because object properties are hidden within the travel bags, and not directly exposed to clients as plain methods.

Another negative consequence is that you may end up looking for objects in bags at runtime, and not at compilation time, so you may make development cycles longer.

As a good technique, consider extracting some properties from the Lookup, and exposing them as plain methods in your API once you think things stabilize.

Known uses

This technique is used all along the NetBeans codebase, allowing to decouple lots of different development teams by building APIs that are more easy to modify without interrupting other teams very frequently.

A good real-life example of the technique is NetBeans's Project class in the NetBeans Project API.

That's all, folks!

So that's all, folks, for now. On next entries we'll see some other useful tecniques using the NetBeans Lookup Library.

Happy Netbeans-ing, Antonio

blog comments powered by Disqus