Using the Strategy Design Pattern for Sorting POJOs Blog



    What Will We Sort?
    First Implementation
    Second Implementation
    Applying the Strategy Design Pattern

    I wouldn't be exaggerating if I said that all of us use POJO's—"Plain Old Java Objects"— in our everyday application development. We use them with Hibernate or with entity beans, sometimes we use them as simple transfer (value) objects, and we use them while creating domain models.

    But what is POJO itself? This term appeared not so long ago and was credited to Martin Fowler, who provides his own explanation of POJO. You can think of POJO as a fancy name denoting a normal Java object that is not a JavaBean, an entity bean, or a session bean, and does not serve any other special role or implement any special interfaces of any of the Java frameworks.

    Many people on discussion forums have asked how to sort POJOs using only core J2SE, since Java provides an excellent way to sort small amounts of data. For larger data-sorting tasks, especially with data that has been extracted from a database, it is more efficient to make the database perform the sort in the first place. However, that discussion goes beyond the scope of this article. Let's focus on sorting your data in Java.

    This article describes an approach that's flexible and can fulfill almost any need. The Strategy design pattern will help us to make it even more powerful and convenient. All we need is Java 2 Platform, Standard Edition version 1.4.2. This code is that simple!

    What Will We Sort?

    We will sort Arrays of POJOs. But it goes without saying that you can sort Lists, too. Thejava.util.Arrays class contains a number of static methods for sorting arrays, just asjava.util.Collections has sort() methods for Lists, so you shouldn't really worry about getting stuck with only one of them.

    In this article we will provide two very similar sorting mechanisms, each slightly different. In the first implementation we will explicitly set how we want to sort our array of POJOs, and in the second implementation we will add behavior to our POJOs to indicate how they should be sorted. You get to decide which one to use, and this will depend very much on your application's needs and requirements.

    Before we start, let's see what our simple POJO will look like. The Duck class is illustrated in Figure 1.

    Figure 1
    Figure 1. Duck POJO

    As you can see, Ducks from our courtyard have only name, size, and weight. Here is the code for this POJO:

    public class Duck { private String duckName; private int duckWeight; private int duckSize; public Duck(String s, int i, int x) { this.duckName = s; this.duckWeight = i; this.duckSize = x; } public String toString() { return("Duck with name '" + this.duckName + "' has weight " + this.duckWeight + " and size " + this.duckSize + "."); } public String getDuckName() { return this.duckName; } public void setDuckName(String s) { this.duckName = s; } public int getDuckWeight() { return this.duckWeight; } public void setDuckWeight(int i) { this.duckWeight = i; } public int getDuckSize() { return this.duckSize; } public void setDuckSize(int i) { this.duckSize = i; } } 

    I doubt that any comments on this code are needed.

    First Implementation

    As mentioned above, java.util.Arrays has a few static methods to sort arrays, one of which isArrays.sort(Object[] a, Comparator c). This method sorts the specified array of objects according to the order indicated by the specified comparator. All elements in the array must be mutually comparable by the specified comparator. TheComparator object has to implement thejava.util.Comparator interface and its methodcompare(Object o1, Object o2). This method returns -1 (if o1 is less than o2), 0 (ifo1 and o2 are equal), or 1 (ifo1 is greater than o2). This way we define our own rules for sorting the list of objects.

    You've probably figured out that to sort on each field of the POJO, we will need to create our own comparator implementations. Here is an example of one, for sorting by size:

    import java.util.Comparator; public class SortBySize implements Comparator { public int compare(Object f, Object s) { Duck o1 = (Duck) f; Duck o2 = (Duck) s; if (o1.getDuckSize() < o2.getDuckSize()) return -1; if (o1.getDuckSize() == o2.getDuckSize()) return 0; return 1; } } 

    Using the comparator is simple. Here is a test:

    ... Duck d1 = new Duck ("Ducky", 50, 33); Duck d2 = new Duck ("Greenie", 44, 24); Duck d3 = new Duck ("Tutsie", 1, 105); Duck d4 = new Duck ("Lisie", 911, 87); Duck[] ducks = { d1, d2, d3, d4 }; ... Arrays.sort(ducks, new SortBySize()); for (int i = 0; i < ducks.length; i++) { System.out.println(ducks[i]); } ... 

    To test this, unzip the source code for this article, change the directory to example1, and compile the test class with:

    javac -classpath .
    You then run the code with:
    java TestDrive

    You will see the output shown in Figure 2:


    Figure 2
    Figure 2. A "test drive" of the first sorting implementation; click the image for a full-sized screen shot.

    That's all we need!

    But that's not the end of the article. We may encounter sorting situations in which we don't know how to sort the array, and our array needs to know that. This is where our second implementation comes into play.

    Second Implementation

    For our second implementation we will take advantage of thejava.lang.Comparable interface. It defines the methodcompareTo(Object o), which compares theComparable object with the specified object for ordering. It should return a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object, as does java.util.Comparator'scompare(). Classes in core Java that have a "natural ordering" implement Comparable, like the numeric wrapper classes (Integer, Float,Double), String, and a few others.

    To use this interface we will have to modify our POJO a bit:

    public class Duck implements Comparable { ... public int compareTo(Object ob) { DuckSortable o2 = (DuckSortable) ob; if (this.getDuckSize() < o2.getDuckSize()) return -1; if (this.getDuckSize() == o2.getDuckSize()) return 0; return 1; } ... } 

    As you can see, our compareTo(Object o)implementation is based on the duck's size. Now, to sort our array of ducks, we can use the following piece of code:

    ... Duck d1 = new Duck ("Ducky", 50, 33); Duck d2 = new Duck ("Greenie", 44, 24); Duck d3 = new Duck ("Tutsie", 1, 105); Duck d4 = new Duck ("Lisie", 911, 87); Duck[] ducks = { d1, d2, d3, d4 }; ... Arrays.sort(ducks); for (int i = 0; i < ducks.length; i++) { System.out.println(ducks[i]); } ... 

    Everything will be sorted by size. But wait a minute. How can we change the sort behavior? It looks like we have created a much bigger headache for ourselves than we started with. Don't worry—this is where the Strategy design pattern comes into play.

    Applying the Strategy Design Pattern

    The Strategy design pattern basically consists of decoupling an algorithm from its host, and encapsulating the algorithm into a separate class. More simply put, an object and its behavior are separated and put into two different classes. This allows you to switch the algorithm that you are using at any time.

    When you have several objects that are basically the same and differ only in their behavior, it is a good idea to make use of the Strategy pattern. With Strategies, all you need to do is switch the object's strategy, and it will immediately change how it behaves. Obviously, the Strategy pattern sounds like a solution to our sorting problem.

    In general, the Strategy pattern can be represented with the UML diagram in Figure 3:


    Figure 3
    Figure 3. The Strategy design pattern; click the image for a full-sized screen shot.

    Let's see how we can apply this pattern to our second implementation, which lacks exactly this kind of flexibility. Ouralgorithm is the code that we have inside thecompareTo() method. We could have many implementations of this method: one for sorting by size, another for sorting by weight or by name. We can have even complex sortings by multiple fields.

    Each sorting implementation will be placed in a separate class, and these are tied together by a SortStrategyinterface that all of them implement. Now, we can describe our second implementation of POJO sorting with this UML diagram:


    Figure 4
    Figure 4. Applying the Strategy pattern for our second implementation; click the image for a full-sized screen shot.

    You may have noticed that a new interface, calledDuckSortable, has appeared. It's not required; we can work without it, but keeping everything in the Duck class is a bad practice. I prefer to follow the well-known design principle: "Program to an interface, not an implementation."

    Here are code updates for our Duck class:

    public class Duck implements DuckSortable { ... private SortStrategy sortStrategy; ... public int compareTo(Object ob) { return this.sortStrategy.sortingAlgorithm(this, ob); } ... public SortStrategy getSortStrategy() { return this.sortStrategy; } public void setSortStrategy(SortStrategy s) { this.sortStrategy = s; } ... } 

    The class implementing the "sort by size" will look like this:

    public class SortBySize implements SortStrategy { public int sortingAlgorithm(Object f, Object s) { DuckSortable o1 = (DuckSortable) f; DuckSortable o2 = (DuckSortable) s; if (o1.getDuckSize() < o2.getDuckSize()) return -1; if (o1.getDuckSize() == o2.getDuckSize()) return 0; return 1; } } 

    That's all we need. To do the sort, you'd do the following:

    ... DuckSortable d1 = new Duck ("Mupsy", 4350, 145); DuckSortable d2 = new Duck ("Yaska", 464, 2344); DuckSortable d3 = new Duck ("Alexie", 541, 1405); DuckSortable d4 = new Duck ("Gismo", 4586, 55); DuckSortable[] ducks = { d1, d2, d3, d4 }; ... // here we change SortBySize strategy, this can be done anywhere! for (int i = 0; i < ducks.length; i++) { ducks[i].setSortStrategy (new SortBySize()); } ... Arrays.sort(ducks); for (int i = 0; i < ducks.length; i++) { System.out.println(ducks[i]); } ... 

    The code above shows only the sort-by-size implementation, however the example code includes implementations of other kinds of sorts along with tests for all of them. To test the whole sample, you can unzip the source code, change the directory to example2, and type:

    javac -classpath .

    and then

    java TestDrive

    The output will look like Figure 5.


    Figure 5
    Figure 5. A "test drive" of the second sorting implementation; click the image for a full-sized screen shot.


    I hope this article will be helpful in your applications, allowing you to create your own custom sorts and easily operate with your POJOs. I should mention that for sorting POJOs, we also can use a third-party comparator calledBeanPropertyComparator. It is a helpful tool, but I think it's better to stay within the range of standard Java, and use your own code.