Using a Prototype Registry in Python
The Prototype Pattern gets more useful when one prototype is no longer enough. Copying a single object is easy to understand. The more interesting question is what happens when a program needs several stored templates and has to choose between them while it is already running.
That is where a prototype registry helps. Instead of cloning whatever object happens to be nearby, the program keeps a collection of known prototypes and selects the right one by name or role. This article is not the general introduction to Prototype, and it is not the copy-behavior article either. Its job is narrower: runtime selection, storage, and cloning through one organized registry.
From One Prototype to Several
A single prototype is easy to picture. You have one object, you copy it, and then you customize the copy. But real programs often need different starting shapes for different situations. A document template is not the same as an invoice template. A basic UI preset is not the same as a danger-state preset. Once several known templates exist, the program needs a way to keep them in one place and choose among them cleanly.
That is the job of the registry. It gives the templates a home and gives the rest of the program one place to ask for them.
A Simple Prototype Registry
import copy
class PrototypeManager:
def __init__(self):
self._prototypes = {}
def register_prototype(self, name, prototype):
self._prototypes[name] = prototype
def clone(self, name, **kwargs):
prototype = self._prototypes.get(name)
if prototype is None:
raise ValueError(f"Prototype '{name}' not found.")
cloned_object = copy.deepcopy(prototype)
cloned_object.__dict__.update(kwargs)
return cloned_object
This changes the shape of the pattern in an important way. The registry owns the collection of templates. The prototype objects still define the starting shape. The caller only needs to know which prototype to request and what values should change after cloning.
A Small Object to Clone
class PrototypeObject:
def __init__(self, name):
self.name = name
def __str__(self):
return f"PrototypeObject instance with name: {self.name}"
This example is intentionally small. The point is not the object itself. The point is the registry’s role in storing and selecting it.
Registering and Cloning
manager = PrototypeManager()
manager.register_prototype("object1", PrototypeObject("Object 1"))
manager.register_prototype("object2", PrototypeObject("Object 2"))
obj1 = manager.clone("object1", name="Customized Object 1")
obj2 = manager.clone("object2", name="Customized Object 2")
print(obj1)
print(obj2)
The important idea here is not just that the objects were copied. It is that the program was
able to choose a prototype at runtime and then customize the clone after the choice was made.
That is what separates a registry from a one-off call to deepcopy().
Why a Registry Helps
A prototype registry is useful when the choice of starting object belongs to the running program, not to one fixed line of constructor code.
That makes it a good fit for document templates, UI presets, configuration templates, seeded workflow objects, and similar situations where several known starting shapes exist and the program needs to select one dynamically.
Without a registry, that selection logic often gets scattered into conditionals across the project. With a registry, the templates are centralized and the selection becomes easier to reason about.
Customization After Cloning Is Part of the Pattern
Beginners sometimes focus only on the copy and forget the second half of the pattern. Prototype is not just about duplication. It is about duplication followed by adjustment. The clone should start from a useful base state, then become the specific object needed for the current case.
That is why the registry’s clone() method updates the copied object with keyword
arguments. The copy gives you the base shape. The customization gives you the final object.
Why Deep Copy Still Matters Here
The registry example uses copy.deepcopy() for a reason. If the prototype contains
nested mutable data, a shallow copy can leave the original and the clone tied together in ways
that make the registry feel unreliable. A deep copy usually better matches the expectation that
each requested object should stand on its own after cloning.
That is not because deep copy is always better in every program. It is because a registry often implies reusable templates that can be cloned many times. Shared nested state usually makes that reuse harder to trust unless it is deliberate.
What the Registry Owns and What It Does Not
The registry owns storage and selection. It does not replace the prototype object itself. The prototype still provides the starting state. The registry simply gives the application a clean way to find the right starting state at the right time.
That distinction matters because it keeps the design readable. If the registry starts becoming a place for unrelated behavior, it stops being a registry and starts becoming a vague manager object.
About Using super() in Prototype Code
A smaller question sometimes appears around this pattern: can you use super() inside
prototype-related code? Technically, yes. But it is usually not the important question.
super() belongs mainly to inheritance and method resolution. Prototype belongs mainly
to copying and cloning. Those concerns can coexist in one project, but they are not the same
structural idea. In practice, the clearer question is not “Can I use super() here?” It is “Is
this code about inheritance or about cloning?” Mixing those concerns too loosely usually makes
the design harder to read.
What a Beginner Should Keep
A prototype registry is a useful extension of the Prototype Pattern when a program needs more than one reusable template. It lets you register known prototypes, select one at runtime, clone it, and then customize the clone for the current situation. That is a more practical structure than simply copying one object in isolation.
The important distinction is that the registry manages selection, while the prototype still provides the starting state. Once that is clear, the pattern becomes easier to use without letting it turn into a vague container for everything.
Further Reading
If you want the broader “when does Prototype help?” article next, read When the Prototype Pattern Starts Making Sense .
If you want the technical follow-up on clone behavior next, read Shallow Copy vs Deep Copy in the Prototype Pattern .
Comments