Prototype?
Your probably thinking of this:
or maybe even this:
I would love to make an article about how amazing Prototype the game is. But unfortunately we are on a design pattern series. Instead we are going to cover the Prototype Design Pattern.
Today you will learn:
- Core concepts of the Prototype Pattern.
- Simple implementations of the Prototype Pattern.
- Opportunities to use the Prototype Pattern.
- Prototype pattern pros and cons.
Definition
The Prototype Pattern is a creational design pattern that allows us to clone objects without having to depend on their underlying classes.
But why would we want to clone objects?
Well, there might be certain situations where you might need to clone an object:
- Laziness: Creating a new object might be a hassle, you might need to pass in some parameters to the constructor or call methods to set some fields. Sometimes it's easier to simply make a clone of an object that you want.
-
Business Logic: Sometimes the logic of the business simply requires you to clone objects. For example, your working on a map app that finds for you the best path from A to B using a car. The program may clone the
Car
object, and have them all go to separate paths and pick the best. -
Defensive Copying: Let's say for example you want to return the date of birth of a
Person
at that moment. It would be wise to create a clone ofPerson
first and then take its date of birth because there is a slight chance that it would be modified by some other code. - Snapshot: Cloning allows you to have snapshots of objects, making some sort of history that you can traverse later on.
PS. Cloning is not the same thing as copying. There are in-fact two types of copying:
- Shallow Copying: Copying the reference of an object. Basically working on the copy, you are still working on the same instance of the objects.
- Deep Copying: When you create a whole new instance of an object. The copy lives independently from the original object. This is basically cloning and the premise of Prototype Pattern.
Problem
Let's imagine that your working on a blogging platform where people can create and share their articles. One day, your product owner comes and tells you that you will have to implement the duplicate article feature.
You say it's pretty simple, and begin working.
You come up with a naive solution:
class Article {
public title;
public body;
private created_at;
constructor(title, body, created_at){
this.title = title;
this.body = body;
this.created_at = created_at;
}
}
class Client {
Article article;
constructor(Article article){
this.article = article;
}
public Article duplicate(){
const clone = new Article();
clone.title = this.article.title;
clone.body = this.article.body;
}
}
Do you see the problem?
We created a new Article
object and copied the title and body from the initial object to the copied one, but what about the created_at
field?
We can't simply copy it over because it's a private field. A solution would be to simply make it public but this is bad practice.
Another problem is that our client is now coupled together with the Article
class, if we want to copy an object of another class, we will have to create this whole process again, but for a different class.
We can do better.
Solution
The prototype pattern delegates the cloning to the actual object.
But first let us create a common interface for all objects that are clonable.
interface Cloneable {
public clone()
}
Now that that's done let us refactor our Article
class to use this interface.
class Article implements Cloneable {
public title;
public body;
private created_at;
constructor(title, body, created_at){
this.title = title;
this.body = body;
this.created_at = created_at;
}
public clone(){
const clone = new Article(this.title, this.body, this.created_at);
return clone;
}
}
Our Article
class now has a clone method which returns for use a clone of the current object.
In our Client
class we can simply:
class Client {
Article article;
constructor(Article article){
this.article = article;
}
public Article duplicate(){
return this.article.clone();
}
}
Simple enough, this is the basic premise of the prototype pattern. Keep in mind that this is already implemented out of the box in most programming languages, so you don't have to implement this yourself, but it's good to know how things work under the hood.
When to use this pattern?
- Use the prototype pattern when you client doesn't need to depend on concrete classes to copy objects.
- Use the pattern when you want to limit the number of subclasses that differ only in their initial field values.
Pros
- You can clone objects more conveniently.
- Your client code doesn't have to depend on the concrete class of the object.
- You get rid of repeated initialization code, instead you can simply clone objects.
- You get an alternative to inheritance, instead of creating a bunch of classes with different configs. Just have one class and copy it with different configs each time.
Cons
- Cloning complex objects may be very tricky and lead to spaghetti code.
Conclusion
This is the simplest design pattern we have covered so far, but don't forget:
"Knowing is Half the Battle"
Take a step further and see ways in your own projects where you would use this pattern.
Today you learned:
- The prototype pattern.
- Implementations of the prototype pattern.
- To recognize opportunities where the Prototype pattern can be used.
- It's pros and cons.
Thanks for reading!
Further Readings
If you want to learn more about the design patterns, I would recommend Diving into Design Patterns. It explains all 23 design patterns found in the GoF book, in a fun and engaging manner.
Another book that I recommend is Heads First Design Patterns: A Brain-Friendly Guide, which has fun and easy-to-read explanations.
Top comments (3)
Should't you implement Cloneable?
My bad didn't realize :D
very informative