Navigation

RSS 2.0 New Entries Syndication Feed Atom 0.3 New Entries Syndication Feed

Show blog menu v

 

General

Use it

Documentation

Support

Sibling projects

RIFE powered

Valid XHTML 1.0 Transitional

Valid CSS!

Blogs : Archives

< Fix for slower startup and shutdown times since Snow Leopard 10.6.1   Reviving RIFE, starting work on version 2 >
The sanitization of Ehcache's Cache constructors

We've all been there, you start with a class and a simple constructor that makes total sense. As time goes by, you keep adding features and the list of constructor arguments grows … and grows … until … it becomes unusable. This is exactly what happened with the Cache class in Ehcache.

The version 1.0 constructor looked like this:

public Cache(String name, int maxElementsInMemory, 
             boolean overflowToDisk, boolean eternal, 
             long timeToLiveSeconds, long timeToIdleSeconds)

However, with version 1.7 this turned into:

public Cache(String name, int maxElementsInMemory,
             MemoryStoreEvictionPolicy memoryStoreEvictionPolicy, 
             boolean overflowToDisk, String diskStorePath, 
             boolean eternal, long timeToLiveSeconds,
             long timeToIdleSeconds, boolean diskPersistent, 
             long diskExpiryThreadIntervalSeconds, 
             RegisteredEventListeners registeredEventListeners, 
             BootstrapCacheLoader bootstrapCacheLoader,
             int maxElementsOnDisk, int diskSpoolBufferSizeMB, 
             boolean clearOnFlush,
             boolean isTerracottaClustered,
             String terracottaValueMode, 
             boolean terracottaCoherentReads)

There are a lot of downsides to relying on constructor parameters like this:

  • the author of the class needs to continue to overload the constructors to add new parameters
  • users need to decide which overloaded constructor they want to use
  • users need to figure out what sensible default values are for the values that they're not interested in
  • it's very easy to accidentally put a value at the wrong place and provide wrong parameters
  • reading the constructor afterwards is a nightmare and can't be done without looking at the Javadocs at the same time, for instance:
new Cache("myCache", 10000, MemoryStoreEvictionPolicy.LRU, false, 
          null, true, 60, 30, false, 0, null, null, 0, 0, false, 
          true, "identity", true)

However there some advantages:

  • you can enforce which parameters are mandatory
  • at instantiation you can validate the constructor parameters
  • at instantiation you fully set up the instance without requiring users to call an initialization method

So, we decided to change the Cache constructor approach in Ehcache 2.0 and use a builder pattern with a fluent interface, keeping the advantages of the parameters approach but solving all the problems.

The version 2.0 constructor looks like this:

new Cache(CacheConfiguration config)

We then created a constructor in CacheConfigurator with the strict minimal number of parameters that are required for a cache to function:

public CacheConfiguration(String name, int maxElementsInMemory)

All the other parameters are implemented as fluent interface methods as well as regular setters and getters so that instances of the class can be used as beans:

public final CacheConfiguration clearOnFlush(boolean clearOnFlush) {
  setClearOnFlush(clearOnFlush);
  return this;
}

This allows the example above to be rewritten like this:

new Cache(new CacheConfiguration("myCache", 10000)
      .eternal(true)
      .timeToLiveSeconds(60)
      .timeToIdleSeconds(30)
      .terracotta(new TerracottaConfiguration()
          .clustered(true)
          .valueMode(ValueMode.IDENTITY)));

The end results are that:

  • we can continue to expand the CacheConfiguration class without ever having to change the constructors of the Cache class
  • the user doesn't have to figure out the defaults for parameters he doesn't care about
  • the configuration parameters are self-explanatory by simply looking at the available API methods
  • anyone can read the usage of any Cache constructor and understand what each configuration value's purpose is
posted by Geert Bevin in Ehcache on Feb 17, 2010 6:34 PM : 7 comments [permalink]
 

Comments

Re: The sanitization of Ehcache's Cache constructors
And the code sanity gods smiled upon us.... :) Now you can avoid those checkstyle errors! ;)
Re: The sanitization of Ehcache's Cache constructors
In your CacheConfiguration example, it turned out that only 2 of the 18 or so constructor args were required. It seems that the Cache object could have been modified to have the same 2 arg constructor, with defaults for the other 16 args, and the same sort of override system.

new Cache("myCache", 10000))
      .eternal(true)
      .timeToLiveSeconds(60)
      .timeToIdleSeconds(30)
      .terracotta(new TerracottaConfiguration()
          .clustered(true)
          .valueMode(ValueMode.IDENTITY)
      );


However, I suppose that using a CacheConfiguration object allows for a cleaner API on the Cache object itself separating configuration related methods out.
Re: The sanitization of Ehcache's Cache constructors
Oh, by the way, point well taken on insanely long constructors, and going minimalist with sane defaults which are overridable!
Re: The sanitization of Ehcache's Cache constructors
You suppose correctly and another reason was that a CacheConfiguration object already existed before, it just was used as a helper class to build a cache as opposed to allowing it to be provided to a cache directly. This also makes the life of existing users easier.

Another reason through is that you don't want to pollute the cache interface with configuration. A lot of the configuration is specific to the implementation of the cache and doesn't really make sense in the general interface.
Re: The sanitization of Ehcache's Cache constructors
I completely agree that this is the correct way to go - often it is nice to be able to declare the majority of fields in class "final"; on the downside, you do end up with a huge constructor argument list then though. For such objects, the best thing you can do is provide a builder with chainable setters.

(Even a builder wouldn't be necessary in lots of cases *if* Java supported both named arguments on invocation, and default values for optional arguments, which is basically what you implement via the builder...)
Re: The sanitization of Ehcache's Cache constructors
Speaking of chainable setters, as I tried to use this api I accidentally did:

new Cache(new CacheConfiguration("foo", 100).setEternal(true)))

However, setEternal() doesn't return "this" so can't be chained. It would be nice if the set methods did return this so they could be chained OR if the javadoc for setEternal() referred you to eternal() which you are less likely to immediately notice.
Re: The sanitization of Ehcache's Cache constructors
Problem with returning this from setters is that they're not considered part of the JavaBeans spec anymore and thus can't be used as real setters anymore. The bean inspection methods enforce a void return type.

Add a new comment

:) ;)
=) :-)
:'( :(
:/ :D
:| :p
:o 8)
Your email address will not be displayed at anytime on any page.
Only provide your email address if you'd like updates on this entry
and it's comments by email.
Please answer this simple math question:
12 + 3 = 
 
 
  

Manage subscription

Remove email:
 

< Fix for slower startup and shutdown times since Snow Leopard 10.6.1   Reviving RIFE, starting work on version 2 >
 
 
 
Google
rifers.org web