Architecture & DesignWorking With Design Patterns: Odds and Ends

Working With Design Patterns: Odds and Ends

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

The Wikipedia page on Design Patterns contains a nice classification table for about 40 different patterns. I’ve discussed about 25 of these patterns in other articles appearing at Developer.com. My articles have covered all of the 23 patterns appearing in the book Design Patterns, plus I’ve covered the Null Object pattern, which doesn’t appear in the Wikipedia classification table.

I’ve chosen not cover the ten concurrency patterns that the Wikipedia table lists. I’ve also chosen not to cover the two visitor pattern variants listed—single-serving visitor and hierarchical visitor. Tackling variants on patterns can quickly lead down into a spider’s nest of alternatives. For example, there are additional state pattern variants (two-level and three-level, to name two), and there’s at least one other visitor pattern variant.

There have been conferences and books on patterns, each introducing new and relevant software design patterns. I even authored a book covering low-lying coding constructs that I subtitled “Patterns for Implementation.” The list of patterns at Wikipedia could easily grow by dozens and probably hundreds. Are all these design patterns? Yes, given a broad definition. They are “solutions to problems in a context,” to re-use the typical phrase for describing patterns, and they have proven useful to someone.

The point is that there is no canonical list of patterns. Much as it is not valuable to approach a problem from a pattern-first perspective (“I have a pattern, what problem can I find that it solves” is a backward way of thinking), it’s not valuable to debate how many patterns there really are. A better idea: Think of patterns as captured knowledge and experience of other developers—a form of reuse! If someone else’s solution solves a problem you have, do you really care whether it’s technically a pattern or not?

Visitor variants and concurrency patterns aside, I thought I’d cover the few other patterns that remain in the Wikipedia pattern list. Some of these are trivial concepts; others are full-fledged patterns.

Specification

Challenge: How do you select objects based on ever-growing criteria? Suppose you’re working with a simple (in-memory) persistence class like HoldingsAccess (see Listing 1).

Listing 1: HoldingsAccess.

import java.util.*;
import util.*;
import domain.*;

public class HoldingsAccess {
   private static MultiMap<String,Holding> holdings =
      new MultiMap<String,Holding>();

   public static void deleteAll() {
      holdings.clear();
   }

   public void save(Holding holding) {
      holdings.put(holding.getBook().getClassification(), holding);
   }

   public List<Holding> findAll(String classification) {
      List<Holding> results = holdings.get(classification);
      if (results == null)
         return new ArrayList<Holding>();
      return results;
   }

   public Collection<Holding> getAll() {
      return holdings.values();
   }

   public Holding find(String barCode) {
      List<Holding> results =
         holdings.get(classificationFrom(barCode));
      if (results == null)
         return null;
      for (Holding holding: results)
         if (holding.getBarCode().equals(barCode))
            return holding;
      return null;
   }

   private String classificationFrom(String barCode) {
      int index = barCode.indexOf(Holding.BARCODE_SEPARATOR);
      return barCode.substring(0, index);
   }

   public List<Holding> findByTitle(String title) {
      List<Holding> results = new ArrayList<Holding>();
      for (Holding holding: getAll())
         if (holding.getBook().getTitle().equals(title))
            results.add(holding);
      return results;
   }

   public List<Holding> findByAuthor(String author) {
      List<Holding> results = new ArrayList<Holding>();
      for (Holding holding: getAll())
         if (holding.getBook().getAuthor().equals(author))
            results.add(holding);
      return results;
   }
}

A Holding encapsulates a Book, which in turn encapsulates information such as the book’s author, title, and year published. Users typically want the ability to search on any of these holding attributes. The HoldingsAccess class contains two such search methods, findByTitle and findByAuthor, each returning a list of matching Holding objects.

You can see where this will head as you add attributes such as media type or keyword to Book. To support searching on that new field, you must change the existing HoldingsAccess class. What you’d prefer is a solution that allows you to close off any changes to HoldingsAccess.

By using the specification design pattern, instead, you create an interface that represents the selection criteria:

public interface HoldingSpecification {
   boolean isSatisfiedBy(Holding holding);
}

A specification implementation provides the selection criteria in the isSatisfiedBy method:

public class AuthorSpecification implements Specification {
   private final String author;

   public AuthorSpecification(String author) {
      this.author = author;
   }

   @Override
   public boolean isSatisfiedBy(Holding holding) {
      return author.equals(holding.getBook().getAuthor());
   }
}

Here’s another specification implementation that supports selecting by matching book title:

public class TitleSpecification implements Specification {
   private final String title;

   public TitleSpecification(String title) {
      this.title = title;
   }

   @Override
   public boolean isSatisfiedBy(Holding holding) {
      return title.equals(holding.getBook().getTitle());
   }
}

You conceivably could eliminate a bit of redundancy by factoring construction of both of these specifications into a common superclass.

With the specification classes in place, the code in HoldingsAccess simplifies. The single method findBy covers any specification passed to it:

public List<Holding> findBy(Specification specification) {
  List<Holding> results = new ArrayList<Holding>();
  for (Holding holding: getAll())
    if (specification.isSatisfiedBy(holding))
      results.add(holding);
  return results;
}

The HoldingsAccess class is now closed to changes in Holding—adding a new Holding field now requires adding a new Specification implementation. The elimination of impact to HoldingsAccess meets the open-closed principle: Modules should be closed for modification, but open to extension.

Lazy Initialization

Lazy initialization means that you defer initializing a field to a useful value until the time it’s first needed. It is a performance optimization.

A library system search returns a list of Book objects. The system presents the user with a list displaying basic book information—author, title, year published, and so on. When the user selects a book for further information, the system shows an image of the front cover of the book. Listing 2 shows an implementation of a Book class that handles loading an image upon construction.

Listing 2: Book.

import java.awt.image.*;
import java.io.*;
import javax.imageio.*;

public class Book {
   private final String author;
   private final String title;
   private final String classification;
   private final String year;
   private BufferedImage coverImage;

   public Book(String author, String title, String classification,
               String year, String imageFilename) {
      this.author = author;
      this.title = title;
      this.classification = classification;
      this.year = year;
      loadCoverImage(imageFilename);
   }

   public String getClassification() {
      return classification;
   }

   public String getAuthor() {
      return author;
   }

   public String getTitle() {
      return title;
   }

   public String getYear() {
      return year;
   }

   @Override
   public String toString() {
      return author + " " + title + " " + year + " " +
         classification;
   }

   public BufferedImage getCoverImage() {
      return coverImage;
   }

   private void loadCoverImage(String imageFilename) {
      try {
         coverImage = ImageIO.read(new File(imageFilename));
      } catch (IOException e) {
         throw new RuntimeException(e);
      }
   }
}

Loading an image is slow enough that you don’t want to incur the cost unless necessary. Imagine that a search returns 500 books. If each image takes “only” 32ms to load (the time it took on my admittedly slow laptop), and the search is single threaded, a user will wait 16 seconds for the search to complete. Even if the search is multi-threaded, the loaded images will quickly bloat the memory space. A better solution is to load images only when the user needs to actually see them. Listing 3 shows an updated Book class that takes advantage of lazy initialization.

Listing 3: Using Lazy Initialization.

import java.awt.image.*;
import java.io.*;
import javax.imageio.*;

public class Book {
   private final String author;
   private final String title;
   private final String classification;
   private final String year;
   private BufferedImage coverImage;
   private final String imageFilename;

   public Book(String author, String title, String classification,
               String year, String imageFilename) {
      this.author = author;
      this.title = title;
      this.classification = classification;
      this.year = year;
      this.imageFilename = imageFilename;
   }

   public String getClassification() {
      return classification;
   }

   public String getAuthor() {
      return author;
   }

   public String getTitle() {
      return title;
   }

   public String getYear() {
      return year;
   }

   @Override
   public String toString() {
      return author + " " + title + " " + year + " " +
         classification;
   }

   public BufferedImage getCoverImage() {
      if (coverImage == null)
         loadCoverImage(imageFilename);
      return coverImage;
   }

   private void loadCoverImage(String imageFilename) {
      try {
         coverImage = ImageIO.read(new File(imageFilename));
      } catch (IOException e) {
         throw new RuntimeException(e);
      }
   }
}

The code within getCoverImage determines whether or not the coverImage field has already been initialized. If the field is null, the code calls off to loadCoverImage.

Thus, constructed books will not incur the cost of loading images, but that cost will be incurred when client code actually requests the image by calling getCoverImage. There’s a slight overhead cost: Every time clients call getCoverImage, an additional check against coverImage executes. The overhead is almost never a problem, but it suggests that you probably don’t want to use lazy initialization unless you really need it.

Lazy initialization requires client code to use the getter method. If the client code happens to be within the Book class itself, that means you must change any direct field access against coverImage to instead call the getter. This is a refactoring pattern known as Self-Encapsulate Field [Fowler].

How do you test-drive lazy initialization? There are at least three ideas:

  1. Don’t. It will get tested indirectly by code that requires use of the image.
  2. Do. It’s a performance optimization. Performance is a requirement for which you should write a test. In this case, you could time the call to the getter, and then assert that the code executes within an arbitrarily short period of time. The test should fail; then, you introduce the optimization to get it to pass. Note that you may have some challenges with execution variances on different platforms.
  3. Mock. Isolate the call to load the image to its own class, perhaps named ImageLoader. Inject an instance of this class into each Book object. Write a test to verify when the ImageLoader load operation gets invoked. This has the benefit of fixing the single-responsibility principle violation in Book—the work to load an image is a separate responsibility than tracking the attributes for a Book.

Object Pool

An object pool is a collection of objects that can be “re-used” on demand. Objects in the pool typically survive for the lifetime of an application. The goal of an object pool is to minimize re-creation of frequently created objects of the same type, so as to prevent a large number of objects from being created (which can help improve performance as well as minimize memory usage).

In Java, there are only rare occasions when you’ll want to use an object pool. If you’re looking to improve upon performance by avoiding garbage collection, understand that modern garbage collectors are extremely sophisticated. Your attempts to circumvent Java’s garbage collector and manage memory on your own will most likely not provide the gains you’re looking for, and can actually make things worse. Not to mention that re-using objects that have any real state is a tricky and risky proposition at best.

The typical reasons to use object pooling are to constrain threading and to minimize the number of open database connections. In either of these cases, or in other cases, the need to pool centers around some sort of real physical constraint.

Because thread pools and database connection pools are common needs, you’ll find that you usually don’t need to write your own. The Oracle JDBC drivers, for example, come with a connection pool class. And as of J2SE 1.5, Java contains support for thread pooling.

A key success factor in using object pools is to ensure that the state of each pool object gets reset when it is returned to the pool for future use. Otherwise you can begin to experience defects or memory leaks.

Rather than contrive a use for an object pool, I chose to contrive an implementation of a thread pool, even though the ThreadPoolExecutor is what you really want to use. This code example exists solely to demonstrate a minimalist implementation of a pool. Bells and whistles? It has none, not even a means to terminate gracefully.

A quick explanation of the code in Listing 4: ThreadPool initiates a number of WorkerThread objects, and also creates a master queue to hold on to incoming requests. It then accepts new Runnable “requests” using the enqueue method; these requests get added to the master queue. Each WorkerThread object waits until the queue has a request available. Once a request is available, it gets removed from the queue and executed. Once a request completes processing, the WorkerThread pool object loops back (it’s in an infinite loop) to grab the next request. Use of the BlockingQueue makes the code dramatically simple!

Listing 4: The unnecessary but tiny thread pool.

import java.util.*;
import java.util.concurrent.*;

public class ThreadPool {
   private final BlockingQueue<Runnable> queue =
      new LinkedBlockingQueue<Runnable>();

   public ThreadPool(int size) {
      for (int i = 0; i < size; i++)
         new WorkerThread().start();
   }

   public void enqueue(Runnable runnable) {
      queue.add(runnable);
   }

   class WorkerThread extends Thread {
      @Override
      public void run() {
         while (true)
            nextRequest().run();
      }

      private Runnable nextRequest() {
         while (true)
            try {
               return queue.take();
            } catch (InterruptedException e) {
            }
      }
   }
}

Utility

A utility class is simply a collection of related stateless functions. For example, when writing unit tests, I often find myself needed helper methods to assert against collection contents. Here’s one such CollectionsUtil class:

import java.util.*;
import junit.framework.*;

public class CollectionsUtil {
   static final String NO_ELEMENTS = "no elements";
   static final String MORE_THAN_ONE_ELEMENT =
      "more than one element";
   public static final String EXPECTED =
      "expected element not retrieved";

   public static <T> T soleElement(Collection<T>
         collection) {
      Iterator<T> it = collection.iterator();
      Assert.assertTrue(NO_ELEMENTS, it.hasNext());
      T sole = it.next();
      Assert.assertFalse(MORE_THAN_ONE_ELEMENT, it.hasNext());
      return sole;
   }

   public static <T> void assertSoleElement(Collection<T>
      collection, Object expected) {
      Iterator<T> it = collection.iterator();
      Assert.assertTrue(NO_ELEMENTS, it.hasNext());
      T first = it.next();
      Assert.assertFalse(MORE_THAN_ONE_ELEMENT, it.hasNext());
      Assert.assertEquals(expected, first);
   }

   public static <T> boolean containsExactly(Collection<T>
      collection, T... objects) {
      if (collection.size() != objects.length)
         return false;
      for (int i = 0; i < objects.length; i++)
         if (!collection.contains(objects[i]))
            return false;
      return true;
   }
}

By definition, then, a utility class is comprised only of static methods. It is not instantiated. Access is static-side only:

@Test
public void addBranch() {
   system.addBranch(BranchTest.NAME1);
   CollectionsUtil.assertSoleElement(system.branches(),
                                     BranchTest.BRANCH1);
}

You’ll note that I did not introduce a private constructor, which would enforce the inability to instantiate CollectionsUtil. If it makes you feel better, add one, but generally there’s no possible harm from someone attempting to access these static methods from an instance.

References

  • [Gamma] Gamma, E., et. al. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley Professional, 1995.
  • [Fowler] Fowler, Martin. Refactoring: Improving the Design of Existing Code. Addision-Wesley, 1999.

About the Author

Jeff Langr is a veteran software developer with over a quarter century of professional software development experience. He’s written two books, including Agile Java: Crafting Code With Test-Driven Development (Prentice Hall) in 2005. Jeff contributed to Uncle Bob Martin’s new book, Clean Code (Prentice Hall, August 2008). Jeff has written over 80 articles on software development, with over forty appearing at Developer.com. You can find out more about Jeff at his site, http://langrsoft.com, or you can contact him via email at jeff at langrsoft dot com.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories