The web content discusses the Prototype Design Pattern in Python, presenting a method for efficiently creating object copies without relying on concrete class implementations.
Abstract
The article "Prototype Design Pattern in Python" delves into a creational design pattern that facilitates the quick and efficient copying of objects, particularly useful when dealing with resource-intensive object creation. The Prototype pattern is exemplified through a scenario where a video game hero needs to summon multiple monsters. Initially, a naive approach to instantiating these monsters is shown to be time-consuming. The solution introduces a Prototype class that uses a registry to store and clone objects, significantly reducing the time required for object creation. The Monster class remains unchanged, but the new Prototype class allows for the registration and cloning of objects with the ability to modify attributes of the cloned instances. The article highlights the performance benefits of using the Prototype pattern, its applicability in scenarios involving numerous object copies, and its flexibility in modifying object attributes without affecting the original classes. It also notes the pattern's simplicity and the potential overhead in smaller projects, suggesting its use primarily in larger-scale applications.
Opinions
The author believes the Prototype pattern is particularly beneficial for large projects due to its efficiency in object copying.
The use of time.sleep(1) in the Monster class constructor is a deliberate choice to simulate resource-intensive object creation, emphasizing the performance gains from using the Prototype pattern.
The author suggests that while copy.deepcopy can be used without a Prototype, having a Prototype class provides a centralized location for managing object copies and their modifications.
The article conveys that the Prototype pattern can lead to more flexible code by reducing the need for numerous subclasses.
The author admits that the Prototype pattern may not be suitable for small projects due to the potential increase in code complexity and resource demands.
The author has not personally found the need to use the Prototype pattern in their own projects, implying it might be a niche solution, but still valuable to understand.
You can also find all the code used through this series on GitHub.
The Prototype Pattern is a creational pattern allowing you to create copies of objects in an efficient way, without depending on the implementation of concrete classes.
The objects to be copied are generally created at runtime.
Problems the Prototype can Solve
Imagine you’re building a video game. You want the hero to have a spell to summon 10 similar monsters.
You can do it easily in a naive way:
Let’s imagine our monsters are demanding a lot of resources, so they take some time to be instanced, that’s why I’ve added time.sleep(1) in the constructor.
The time it takes to run the code in the main is approximately 11 seconds. Just to copy 10 orcs, so imagine if we would have to copy 100 or 1000 orcs…
Solution
There are many implementations of the Prototype pattern. It can be a class, a method… It can use a registry or not, etc… I will make a Prototype class working with a registry.
So, the solution is, you create one orc, then you register it with the Prototype pattern, and when you want to copy it, you just call a method of the Prototype to get the right object copied.
Here is the code:
The class Monster remains the same. What changes is that now we have a Prototype class. This class stores objects in a dictionary. You can register or unregister objects.
Then, you can call the clone method to clone objects. It’s efficient because instead of instancing new objects, it gets them from the registry, and makes a deep copy.
And one additional thing: you can modify the instance you get from the copy as you want. That’s why I’ve added this line:
prototype.register_object('orc', orc)
prototype.register_object('pyromancer', pyromancer)
prototype.register_object('black_knight', black_knight)
prototype.register_object('mega_orc', mega_orc)
orcs = [prototype.clone('orc') for _ in range(100)]
pyromancers = [prototype.clone('pyromancer') for _ in range(50)]
black_knights = [prototype.clone('black_knight') for _ in range(20)]
mega_orcs = [prototype.clone('mega_orc') for _ in range(3)]
It just took a few seconds to run this code, even if we have time.sleep(1) in the constructor of Monster . That’s what makes the Prototype pattern so powerful.
Obviously, you can use copy.deepcopy without creating a Prototype. But having the Prototype allows you to work with all the copies from the same code, so you can add exceptions, log messages, or whatever you want when copying without altering all the classes.
Applicability
Use the Prototype pattern when you have a lot of objects to copy.
Use the Prototype pattern when you want to be able to copy objects at runtime with being able to modify their attributes.
Use the Prototype pattern when you don’t want the copy method to be dependent on the implementation of the classes.
Advantages
Easy implementation: unlike all the creational patterns, it’s easy to implement a Prototype and it doesn’t require a lot of classes.
More code flexibility: because you can alter the values of the objects you want to copy, so you don’t have to create tons of subclasses.
Disadvantage
The main disadvantage is that it can make the code more demanding if you don’t work with a lot of objects. So, for small projects, it’s better not to use this pattern.
Final Note
The Prototype is a simple pattern, easy t understand. But it is only useful for large projects. I’ve never needed to use it as my projects are not enough big. But it’s useful to know it for the day you will need it!
To explore the other stories of this story, click below!