March 8, 2021
Hot Topics:

Working With Design Patterns: Prototype

  • By Jeff Langr
  • Send Email »
  • More Articles »

Imagine a library clerk adding stacks of new books and movies into a library system. The clerk uses a scanner to swipe the bar code that appears on the book. (For simplicity's sake, assume that the bar code is the same as the book's classification number.)

For the first copy of a book added to the library, the system makes an external API call to determine basic book information, such as author (or director, for movies), title, and year published. The lookup is slow. Further, the clerk must visually verify the information coming back, and sometimes must correct inaccurate information.

Listing 1 shows an implementation for the catalog based on these requirements. The test demonstrates that client code is responsible for creating book or movie objects and populating them with appropriate data. Presume that this client is where the call to the external API takes place. Once the material object exists, client code adds it to the catalog by calling addNew.

Listing 1: A simple catalog.

// CatalogTest.java
import static org.junit.Assert.*;

import java.util.*;
import org.junit.*;

public class CatalogTest {
   private Catalog catalog;

   public void initialize() {
      catalog = new Catalog();

   public void isEmptyOnCreation() {
      assertEquals(0, catalog.size());

   public void addNewBook() {
      final Book book = new Book("QA123", "author", "title",
                                 "1999", 1);
      assertEquals(1, catalog.size());
      List<Material> materials = catalog.get("QA123");
      assertEquals(1, materials.size());
      assertSame(book, materials.get(0));

// Catalog.java
import java.util.*;

public class Catalog {
   private MultiMap<String, Material> materials =
      new MultiMap<String, Material>();

   public int size() {
      return materials.valuesSize();

   public void addNew(Material material) {
      materials.put(material.getClassification(), material);

   public List<Material> get(String classification) {
      return materials.get(classification);

Many of the added materials are duplicate copies of the same title, where every piece of information remains the same (and you'll assume that the information need not be verified for these copies). The catalog can make a duplicate of an already existing material—the prototype—thus sparing the API call.

The test in Listing 2 presents the scenario where a first book is externally created and then added to the catalog. A second book is scanned. Because the scanned classification represents one that is already in the system, the client can call addCopy with only the classification. Internally, the catalog is responsible for finding the first book and using its information to create a copy.

Listing 2.

public void addCopyViaPrototype() {
   final Book book = new Book("QA123", "title", "author",
                              "1999", 1);


   assertEquals(2, catalog.size());

   List<Material> materials = catalog.get("QA123");
   assertEquals(2, materials.size());
   assertSame(book, materials.get(0));

   Material copy = materials.get(1);
   assertTrue(copy instanceof Book);
   assertEquals("QA123", copy.getClassification());
   assertEquals("author", copy.getAuthor());
   assertEquals("title", copy.getTitle());
   assertEquals("1999", copy.getYear());
   assertEquals(2, copy.getCopyNumber());

A simple implementation, good enough to get the tests to pass, appears in Listing 3. It's not a very good implementation. Never mind that the algorithm for obtaining the copy number is probably inadequate. The real problem is that the copy method is going to get ugly. Once the test supports movies in addition to books, the copy method will require an if statement to check the material type. The code will also start to get messy with the construction details of the various object types. Changes to how books or movies get constructed will ripple into the Catalog class.

Listing 3.

public void addCopy(String classification) {
   List<Material> copies = materials.get(classification);
   materials.put(classification, copy(copies.get(0)));

private Material copy(Material material) {
   Book book = new Book(material.getClassification(),
         material.getCopyNumber() + 1);
   return book;

Page 1 of 3

This article was originally published on April 2, 2008

Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Thanks for your registration, follow us on our social networks to keep up-to-date