The Prototype Pattern (Creational) ©SoftMoore ConsultingSlide 1
Motivational Example Suppose you have a list of Shape objects, which could be circles, rectangles, etc. How would you write the code to duplicate that list? –Note: You are required to duplicate every shape on the list (deep copy), not simply duplicate the references (shallow copy). How can you create a duplicate list without knowing the exact class of each shape in the original list? –multiway switch based on class? (not maintainable) –reflection? (complicated and messy) –prototype pattern? (simple and maintainable) ©SoftMoore ConsultingSlide 2 for (Shape s : list1) list2.add((Shape)s.clone()); cast required since return type of clone() is Object
Summary of Prototype Pattern The prototype pattern allows creation of objects without knowing their exact type. New objects are created by asking existing objects to make copies of themselves. Java supports the prototype pattern via the clone() method, which is declared in class Object, the mother of all classes. ©SoftMoore ConsultingSlide 3
Shallow Copy versus Deep Copy Shallow copy –field-by-field copy –copies references, not values –fast and memory efficient, but the two copies are not independent – changes to one are reflected in the other Deep copy –recursively copy values, not simply references to values –slower and consumes more memory, but the two copies are independent – changes to one have no affect on the other ©SoftMoore ConsultingSlide 4
Shallow Copy Illustrated ©SoftMoore ConsultingSlide 5 :Circle:Rectangle:Circle:Pentagon list1 list2 What happens when you change the radius of one of the circles?
Deep Copy Illustrated ©SoftMoore ConsultingSlide 6 :Circle:Rectangle:Circle:Pentagon list1 :Circle:Rectangle:Circle:Pentagon list2
Prototype Pattern Intent: Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype. Collaborations: A client asks a prototype object to clone itself. Consequences: –Client can create objects without knowing their exact class or creation details –Easy to add and remove objects at run-time. –Reduced number of class and reduced subclassing when compared to other creational patterns. ©SoftMoore ConsultingSlide 7
Prototype Pattern (continued) Structure: ©SoftMoore ConsultingSlide 8 Client operation() Prototype clone() ConcretePrototype1 clone() ConcretePrototype2 clone() p = prototype.clone() return copy of self prototype
Prototype Pattern (continued) Example Application: User-customizable palette of drawing symbols for a graphics application. Implementation Issues: –using a prototype manager (registry of available prototypes) –shallow copy versus deep copy (what can be shared) Support for the Prototype pattern in Java: –the clone() method defined in class Object –use of Cloneable interface to grant permission for cloning ©SoftMoore ConsultingSlide 9
Using a Prototype Manager When the prototypes in a system can be created and destroyed dynamically, consider using a prototype manager, a registry of available prototypes. Clients won’t manage prototypes themselves but will store and retrieve them from the registry using keys. A prototype manager is an associative store that returns the prototype matching a given key. ©SoftMoore ConsultingSlide 10
©SoftMoore ConsultingSlide 11 The clone() method is used to create a copy of an existing object. Shallow copy versus deep copy (field-by-field copy versus recursive copy) Shallow copy is acceptable for primitive types and immutable fields (e.g., int, String, Double, etc.) The clone() Method in Java e1 :Employee e2 e1 :Employee e2 :Employee
Expectations for the clone() Method For any object x Different objects x.clone() != x Same contents x.clone().equals(x) Same class x.clone().getClass() == x.getClass() Slide 12©SoftMoore Consulting
Slide 13 Implementing the clone() Method To support cloning correctly for subclasses Implement the Cloneable interface. –tagging (a.k.a. marker) interface –no methods; only purpose is to allow calls to clone() method Redefine the clone() method as public. –declared as private in class Object –required even if calling super.clone() is sufficient for implementation Enclose method body in a try/catch block that catches and ignores CloneNotSupportedException (can’t happen since we are implementing Cloneable, but we must still catch it anyway)
©SoftMoore ConsultingSlide 14 Implementing the clone() Method (continued) Call super.clone() to obtain an initial object –checks to see if this object implements the Cloneable interface (if not, throws a CloneNotSupportedException ) –creates a new instance of the class of this object –method clone() in class Object initializes all fields using a shallow copy Perform a deep copy of each mutable field –usually implemented by calling the clone() method for that field reference For primitive types and immutable fields, calling super.clone() is sufficient; i.e., no additional work to implement a deep copy is required.
Calling super.clone() The clone() method of a class usually calls super.clone() to copy fields inherited from the parent class and then does any custom copying procedures for mutable fields declared in the class. Similarly the clone() method of the super class would usually call super.clone(). Eventually the clone() method for class Object is called, which creates a new instance of the original class and performs a field-by field copy (“shallow copy”) to the new instance. ©SoftMoore ConsultingSlide 15
©SoftMoore ConsultingSlide 16 Overriding the clone() Method // In class Manager: Assume that Manager has several // primitive and immutable fields plus a mutable field // named "dept" of class Department. public Object clone() { try { Manager result = (Manager) super.clone(); // must handle mutable field dept result.dept = (Department) dept.clone(); return result; } catch (CloneNotSupportedException ex) { return null; // will never happen } clone() method for Department used to implement clone() method for Manager
Java Serialization and Cloning Java serialization is also a form of cloning. –performs a deep copy Serialization: write object as a sequence of bytes to a stream Deserialization: recreate brand new object on the other end with the original object’s data Serialization can be used for persistence (writing to a file stream) or for socket communication. ©SoftMoore ConsultingSlide 17 Note: Deserialization does not call the default constructor. It simply creates a blank object and fills in the fields with values retrieved via deserialization.
©SoftMoore ConsultingSlide 18 Serialization (continued) Object Serialization Java Application Class A instance int x = 4 B b = Class B instance Serialized Object(s) in Arbitrary File Java Application Class A instance int x = 4 B b = Class B instance DeserializationSerialization JVM External Storage
Using Serializaiton to Implement clone()... ObjectOutputStream out = new ObjectOutputStream(new ByteArrayOutputStream()); out.writeObject(this); out.flush(); ByteArrayInputStream bais = new ByteArrayInputStream(out.toByteArray()); ObjectInputStream in = new ObjectInputStream(bais); return in.readObject(); ©SoftMoore ConsultingSlide 19
When to Use the Prototype Pattern When classes to instantiate are specified at run-time. When you want to create another object at runtime that is a true copy of the object you are cloning. When object is complex and easier to clone. When object is time consuming to instantiate from scratch. When you don’t have access to a constructor for that object. When you have objects that differ in state in small ways. Slide 20©SoftMoore Consulting
References Prototype pattern (Wikipedia) Prototype Pattern (Object-Oriented Design) Prototype Design Pattern (SourceMaking) ©SoftMoore ConsultingSlide 21