Prototype Pattern: Subtle Issues and Best Practices
The Prototype pattern introduces several non-obvious challenges that can cause bugs in production. Understanding these separates candidates who memorize patterns from those who've debugged real implementations.
When an object holds references to external resources (file handles, database connections, sockets), naive cloning creates shared access. For example, if a Document has an open FileHandle, cloning it doesn't create a new file handle. Both original and clone share the same file, causing corruption if both write simultaneously.
If you have Shape ← Polygon ← Rectangle, each level may have fields that need cloning. The Rectangle.clone() must call Polygon.clone() which calls Shape.clone() to ensure all ancestor fields are copied. Forgetting this creates partially initialized clones.
clone() at each level to explicitly copy fields, then chain to parent. Document the cloning contract clearly in the base class.function clone():
cloned = super.clone() // Polygon handles its fields
cloned.width = this.width
cloned.height = this.height
return cloned
Some objects have unique identities (user IDs, transaction IDs) that shouldn't be cloned. If you naively copy all fields, the clone has the same ID as the original, violating uniqueness constraints. This causes database conflicts or logical errors.
clone(), explicitly generate a new ID for the clone. Document which fields are identity vs value in class comments. For example, a Task object's description and priority can be cloned, but its taskId must be freshly generated.First, document cloning behavior at the class level (shallow vs deep, which fields are excluded). Second, provide both shallow and deep clone methods if both are useful (shallowClone() and deepClone()). Third, consider making sensitive or complex classes non-cloneable if cloning semantics are unclear. Fourth, use immutability where possible to avoid deep cloning complexity entirely. Fifth, write unit tests that verify clones are independent (modify clone, assert original unchanged).
If your domain has many of these issues (resource handles, complex inheritance, identity constraints), the maintenance cost of correct cloning may exceed benefits. In such cases, use explicit builder objects or factory methods with clear construction steps. Prototype works best for immutable or nearly-immutable objects with minimal external dependencies.
clone() requires more than 10 lines of logic or extensive documentation, the class is likely too complex for Prototype. Simplify the design or choose a different creational pattern.