October 25, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Pattern Summaries: Prototype

  • September 5, 2003
  • By Mark Grand
  • Send Email »
  • More Articles »

The essence of a pattern is a reusable solution for a recurring problem. A complete pattern will also provide reasons to use and not use the solution, the consequences of using the solution and suggestions on how to implement the solution. The summaries in these articles will just describe the essential problem and its solution. This is the last of a group of patterns that are related to creating objects. The issues that they address involve dynamically deciding which class to instantiate or to which objects an object will delegate a responsibility. These patterns tell us how to structure and encapsulate these decisions. This article discusses a pattern called Prototype. The Prototype pattern enables a class to create objects that implement a known interface by giving it a prototypical instance of each kind of object it will create.

Suppose you are designing a CAD program that allows its users to draw diagrams from a palette of symbols. The program will have a core set of built-in symbols. However, people with different and specialized interests will use the program. The core set of symbols will not be adequate for people with a specialized interest. Those people will want additional symbols that are specific to their interests. Most users of this program will have a specialized interest. It must be possible to provide additional sets of symbols that users can add to the program to suit their needs.

This gives you the problem of how to provide these palettes of additional symbols. You can easily organize things so all symbols, both core and additional, are all descended from a common ancestor class. This will give the rest of your diagram-drawing program a consistent way of manipulating symbol objects. It does leave open the question of how the program will create these objects. Creating objects such as these is often more complicated than simply instantiating a class. It may also involve setting values for data attributes or combining objects to form a composite object.

A solution is to provide the drawing program with previously created objects to use as prototypes for creating similar objects. The most important requirement for objects to be used as prototypes is that they have a method, typically called clone, that returns a new object that is a copy of the original object. Here is a class diagram that shows how this would be organized:


Figure 1. Symbol Prototype.

The drawing program maintains a collection of prototypical Symbol objects. It uses the Symbol objects by cloning them. SymbolBuilder objects create Symbol objects and register them with the drawing program.

All Java classes inherit a method from the Object class called clone. An object's clone method returns a copy of the object. It only does that if the object's class gives permission. A class gives permission for its instances to be cloned if, and only if, it implements the Cloneable interface. Because the Symbol class implements the Cloneable interface, the drawing program is able to clone the Symbol objects that it manages and incorporate those objects into drawings.

Generalizing this, the Prototype pattern enables a class to create objects that implement a known interface by giving it a prototypical instance of each kind of object it will create. It is then able to create new objects by cloning a prototypical instance.

Here is a diagram that shows, for the general case, the organization of the Prototype pattern:


Figure 2. Prototype Pattern.

Below are descriptions of the roles these classes and interface play in the Prototype pattern:

  • Client
    The client class represents the rest of the program for the purposes of the Prototype pattern. The client class needs to create objects that it knows little about. Client classes will have a method that can be called to add a prototypical object to a client object's collection. In the above diagram, that method is indicated with the name registerPrototype. However, a name that reflects the sort of object being prototyped, such as registerSymbol, is more appropriate in an actual implementation.
  • Prototype
    Classes in this role implement the PrototypeIF interface and are instantiated for the purpose of being cloned by the client. Classes in this role are commonly abstract classes with a number of concrete subclasses.
  • PrototypeIF
    All prototype objects implement the interface that is in this role. The client class interacts with prototype objects through this interface. Interfaces in this role should extend the Cloneable interface so that all objects that implement the interface can be cloned.
  • PrototypeBuilder
    This corresponds to any class that is instantiated to supply prototypical objects to the client object. Such classes should have a name that denotes the type of prototypical object that they build, such as SymbolBuilder. A PrototypeBuilder object creates Prototype objects. It passes each newly created Prototype object to a Client object's registerPrototype method.

The code example for this pattern is based on a different scenario. Suppose that you are writing an interactive role playing game. That is, a game that allows the user to interact with simulated characters. One of the expectations for this game is that people who play it will grow tired of interacting with the same characters and want to interact with new characters. For this reason, you are also developing an add-on to the game that consists of a few pre-generated characters and a program to generate additional characters.

The characters in the game are instances of a relatively small number of classes such as Hero, Fool, Villain, and Monster. What makes instances of the same class different from each other is the different attributes values that are set for them, such as the images that are used to represent them, height, weight, intelligence and dexterity.

Below is a class diagram that shows some of the classes involved in the game:


Figure 3. Prototype Example.

Here is the code for the CharacterIF interface, the interface that serves the role of PrototypeIF.

Listing 1: The CharacterIF Interface

public interface CharacterIF extends Cloneable {
    public String getName() ;
    public void setName(String name) ;
    public Image getImage() ;
    public void setImage(Image image) ;
    public int getStrength() ;
    public void setStrength(int strength) ;
    ...
} // interface CharacterIF

Here is the code for the Character class, the abstract class that serves the role of PrototypeIF:

Listing 2: The Character Class

public abstract class Character implements CharacterIFloneable {
...
    /**
     * Override clone to make it public.
     */
    public Object clone() {
        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
            // This should never happen because this class implements
            // Cloneable.
            throw new InternalError();
        } // try
    } // clone()

    public String getName() { return name; }

    public void setName(String name) { this.name = name; }

    public Image getImage() { return image; }

    public void setImage(Image image) { this.image = image; }

...
} // class Character

Most of this is just simple accessor methods. The one less than obvious method is the clone method. All objects inherit a clone method from the Object class. Because that clone method is not public, the character class must override it with a public declaration, just to make it accessible to other classes.

Here is source code for the Hero class, one of the classes that serve in the Prototype role:

Listing 3: The Hero Class

public class Hero extends Character {
    private int bravery;
...
    public int getBravery() { return bravery; }

    public void setBravery(int bravery) { this.bravery = bravery; }
} // class Hero

The Monster class is similar to the Hero class.

Below is the code for the CharacterManager class that serves in the role of Client class:

Listing 4:The CharacterManager Class

public class CharacterManager {
    private Vector characters = new Vector();
...
    /**
     * Return a copy of random character from the collection.
     */
    Character getRandomCharacter() {
        int i = (int)(characters.size()*Math.random());
        return (Character)((Character)characters.elementAt(i)).clone();
    } // getRandomCharacter()

    /**
     * Add a prototypical object to the collection.
     */
    void addCharacter(Character character) {
        characters.addElement(character);
    } // addCharacter(Character)
...
} // class CharacterManager

Here is the code for the CharacterLoader class that fills the role of PrototypeBuilder:

Listing 5: The CharacterLoader Class

/**
 * This class loads character objects and adds them to the
 * the CharacterManager.
 */
class CharacterLoader {
    private CharacterManager mgr;

    /**
     * Constructor
     * @param cm The CharacterManager that this object will work with.
     */
    CharacterLoader(CharacterManager cm) {
        mgr = cm;
    } // Constructor(CharacterManager)

    /**
     * Load character objects from the specified file.
     * Since failure only affects the rest of the program to the extent
     * that new character objects are not loaded, we need not throw any
     * exceptions.
     */
    int loadCharacters(String fname) {
        int objectCount = 0;    // The number of objects loaded
        // If construction of the InputStream fails, just return
        try {
            InputStream in;
            in = new FileInputStream(fname);
            in = new BufferedInputStream(in);
            ObjectInputStream oIn = new ObjectInputStream(in);
            while(true) {
                Object c = oIn.readObject();
                if (c instanceof Character) {
                    mgr.addCharacter((Character)c);
                } // if
            } // while
        } catch (Exception e) {
        } // try
        return objectCount;
    } // loadCharacters(String)
} // class CharacterLoader

About the author

Mark Grand is the author of a series of books titled Patterns in Java. He is currently the chief architect of an application framework for e-commerce called EBox. Mark Grand can be contacted at mgrand@mindspring.com.




Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel