Working With Design Patterns: Prototype
The prototype pattern suggests that the catalog tell the prototype object (in this case, the Book object) to return a clone of itself. In other words, the Book class should do all the work. Adding the Movie type, in turn, suggests that the solution should be polymorphic.
A more robust and refactored solution that incorporates the notion of a prototype appears in Listings 4 (the tests) and 5 (the production code). Listing 6 shows the Material, Book, and Movie classes.
Listing 4: Tests to drive out use of prototype.
import static org.junit.Assert.*; import java.util.*; import org.junit.*; public class CatalogTest { private Catalog catalog; @Before public void initialize() { catalog = new Catalog(); } @Test public void isEmptyOnCreation() { assertEquals(0, catalog.size()); } @Test public void addNewBook() { final Book book = new Book("QA123", "author", "title", "1999", 1); catalog.addNew(book); assertEquals(1, catalog.size()); List<Material> materials = catalog.get("QA123"); assertEquals(1, materials.size()); assertSame(book, materials.get(0)); } @Test(expected=NoExistingCopyException.class) public void addNewCopyThrowsIfNoExistingCopy() { catalog.addCopy("QA123.4"); } @Test public void addCopyViaPrototype() { final Book book = new Book("QA123", "title", "author", "1999", 1); catalog.addNew(book); catalog.addCopy("QA123"); assertEquals(2, catalog.size()); assertMaterialCopies("QA123", 2, book); } @Test public void addMovieCopyViaPrototype() { final Movie movie = new Movie("DD890", "Shining", "Kubrick", "2006", 1, Movie.Format.DVD); catalog.addNew(movie); catalog.addCopy("DD890"); assertEquals(2, catalog.size()); assertMovieCopies("DD890", 2, movie); } private void assertMaterialCopies(String classification, int number, Material material) { List<Material> materials = catalog.get(classification); assertEquals(number, materials.size()); for (int i = 0; i < number; i++) { Material copy = materials.get(i); assertEquals(material.getClass(), copy.getClass()); assertEquals(material.getClassification(), copy.getClassification()); assertEquals(material.getAuthor(), copy.getAuthor()); assertEquals(material.getTitle(), copy.getTitle()); assertEquals(material.getYear(), copy.getYear()); assertEquals(i + 1, copy.getCopyNumber()); } } private void assertMovieCopies(String classification, int number, Movie material) { assertMaterialCopies(classification, number, material); for (Material copy: catalog.get(classification)) assertEquals(material.getFormat(), ((Movie)copy).getFormat()); } }
Listing 5: A Catalog that uses the prototype pattern.
import java.util.*; public class Catalog { private MultiMap<String, Material> materials = new MultiMap<String, Material>(); public void addCopy(String classification) { List<Material> copies = materials.get(classification); if (copies.isEmpty()) throw new NoExistingCopyException(); materials.put(classification, copies.get(0).copy()); } 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); } }
Page 2 of 3
This article was originally published on April 2, 2008