Sunday 20 September 2009

Item 21: Use function objects to represent strategies

This item describes Java's idioms for implementing the Strategy pattern. As Java doesn't support raw function pointers, a function object is an instance of a class whose main (often only) purpose is to implement a single function. A string comparison function is given as an example, one which compares strings by their length:

class StringLengthComparator {
public int compare(String s1, String s2) {...}
}

An object of this class can be passed to a function that sorts strings, and the client uses this comparator object to specify exactly how the strings are to be ordered.

When I first read this far, I thought, this function doesn't need a "this" pointer, so why not make it static? The answer, of course, is that static functions cannot be overridden in Java. Despite wasting CPU cycles passing an unused "this" reference, it makes sense to make it polymorphic. The idiom is to specify an interface containing the desired name of the method to be overridden. For example, java.util.Arrays.sort() takes an array of a template type T and a strategy interface defined like this:

public interface Comparator {
int compare(T t1, T t2);
}

To use such an interface, a client application has the following choices:

1. Define a class that implements the interface, and pass an instance of that class. Typically, strategy classes are stateless, and therefore the class should be a singleton.

2. Use an anonymous class. I see this quite a lot in GUI applications when passing event handlers, etc, where typically the instance of the anonymous class is created at the point where it is passed to a method. However, if this is done in a loop, a new object is created on the stack each time, so consider assigning this object to a variable and reusing that in the loop.

3. Use one of a set of strategy objects that you have predefined in a utility class. Such a utility class contains a number of inner classes and public static final instances of these. The inner classes can be anonymous. If they are named, they can be made private because the users are only interested in instances, not the classes themselves. The inner classes need to be named if they are to implement more than
just the strategy interface (for example, Serializable.) An example of
such a pattern is the Comparator instance CASE_INSENSITIVE_ORDER
exported by the String class.

I found everything in this item made sense, with no major surprises. What does everybody else think?

Klitos Kyriacou

No comments: