A Simple Java Pattern for Cacheable Objects

When creating interactive programs, caching computational results can help to improve performance. In this short post I present a simple java pattern that helps to keep the code organized and correct.

Consider for instance a computationally expensive color palette. It is essentially a mathematical function that takes a value and returns a color. Depending on the complexity of the mapping, the initialization of the palette can be too expensive to perform in each draw step. In my case, it’s also not necessary since the palette depends on the data (min/max values) and a render mode (e.g. unipolar/bipolar, what colors for the ends of the scales, etc.). As long as none of these parameters change, the palette remains the same and can be reused. The cost at which this comes is that one has to assure that the palette is replaced with a fresh palette once the computation parameters change.

In principal, this has to be done before every access to the palette and in potentially many places in the code, which introduces redundancy. Another nuisance is that caching usually introduces additional variables. I added two variables which store for which input parameters the palette was computed.

/** The matrix for which the paint scale was computed. */
private CorrelationMatrix paintScaleMatrix;
/** The render mode for which the paint scale was computed. */
private RENDER_MODE paintScaleRenderMode;

Since we have redundant code that can be put into a method and we have variables, using a class is the object oriented way to go. Since the palette is not the only reusable object I have in my code, I created a simple abstract class to declare a cacheable value.

/**
 * Wrapper class for a value that can be reused as long as it is not invalidated.
 * Created by Carl Witt on 27.10.14.
 */
public abstract class Cacheable<T> {

    /** The value to reuse. */
    protected T cachedValue;

    /** Recomputes the value if it is not valid.
     * @return the new cached value */
    public T get(){
        if(!isValid()) recompute();
        return cachedValue;
    }

    /** @param newValue a value to set the cached value to */
    public void set(T newValue){ cachedValue = newValue; }

    /**
     * Similar to a dirty-bit in a cache.
     * @return whether the cached value is currently valid
     */
    public abstract boolean isValid();

    /** Makes the cached value valid. */
    public abstract void recompute();

}

Note that the initial state of the cached variable is null. The isValid() function should take that into account.

I could now let my palette class implement the abstract methods, but that’s not exactly what I want. Assuming the palette class is reusable, I don’t want to add logic to it that defines whether a specific palette instance is valid in a specific context. The palette shouldn’t have to know about the data that defines its ranges and the render mode that defines its colors.

Instead, I use an anonymous inner class to encapsulate the necessary cache state variables, the cached variable itself and the accessor methods into a single object.

/** encodes 2D values in a single color */
private final Cacheable<MultiDimensionalPaintScale> paintScale = new Cacheable<MultiDimensionalPaintScale>() {

	/** The matrix for which the paint scale was computed. */
	private CorrelationMatrix paintScaleMatrix;
	/** The render mode for which the paint scale was computed. */
	private RENDER_MODE paintScaleRenderMode;

	@Override public boolean isValid() {
		CorrelationMatrix matrix = sharedData.getCorrelationMatrix();
		// the matrix is immutable, so comparing references is faster than comparing with equals()
		return matrix == paintScaleMatrix && renderMode.equals(paintScaleRenderMode);
	}

	@Override public void recompute() {
		CorrelationMatrix matrix = sharedData.getCorrelationMatrix();
		// depending on the render mode, configure the paintscale (expensive for high-resolution color scales)
		Correlogram.this.configurePaintscale(matrix, cachedValue);
		paintScaleMatrix = matrix;
		paintScaleRenderMode = renderMode;
	}
};

In line 10 and 20, the abstract methods are implemented. In line 18 I access a method of the outer class to update the state of the paint scale object. Given that I need to compute paint scales only occasionally now, I could even switch to an immutable paint scale, eliminating the unlovely pass-by-reference return parameter that directly manipulates the given paint scale.

Summing up, the pattern improves consistency because all checks and updates of the cached variable happen in the same place. The abstract class eliminates some code by providing default implementations of get() and set() and the cache variable itself. In my opinion, the pattern improves code structure and readability because all variables and methods concerned with the state of the cache variable are kept in a single place.

Cacheable.java

Leave a Reply

Your email address will not be published. Required fields are marked *