The Prototype pattern was created by Gamma et al. in his book
The Prototype pattern was created by Gamma et al. in his book "Design Patterns: Elements of Reusable Object-Oriented Software", published in 1995. This Prototype pattern is a creational design pattern in software development.
It is used to create new objects by copying existing objects, instead of creating new objects from scratch. The purpose of this pattern is to reduce the complexity of object creation and allow new objects to be created without specifying their classes. It is a good practice for dealing with complex object creation issues, and can be used to facilitate the implementation of shallow and deep object copying mechanisms.
The pattern is implemented by creating a " Prototype " interface that defines a "clone" method for creating copies of an object. Concrete classes implement this method to create copies of themselves. This pattern is useful when creating new objects might be costly or impractical, such as when objects need to be created at runtime or when objects are instantiated dynamically.
The Prototype pattern has several advantages, including:
Below is a simple code example using the Prototype pattern.
// Interface Prototype
class Prototype {
clone() {}
}
// Concrete Prototype
class ConcretePrototypeA extends Prototype {
constructor(name) {
super();
this.name = name;
}
clone() {
return new ConcretePrototypeA(this.name);
}
}
// Usage
let prototypeA = new ConcretePrototypeA("Prototype A");
let copyOfPrototypeA = prototypeA.clone();
console.log(copyOfPrototypeA.name); // Output: "Prototype A"
In this example, the Prototype class is an interface that defines the clone method. The ConcretePrototypeA class is a concrete implementation of the Prototype class and implements the clone method to create a copy of the object.
On usage, a ConcretePrototypeA object is created and stored in the prototypeA variable, and soon after it is cloned to another object and stored in the copyOfPrototypeA variable.
As mentioned earlier, this way of implementing it can be a way of making deep copies, but it is important to remember that this pattern has several ways of being implemented, and some libraries already have implementations of object clones, for example lodash.cloneDeep() .
Simple, right?
Imagine another scenario in which you need to perform a search and insert data via a form through an entry point that is an API.
Api listed was:
Follow the solution below:
// Interface Prototype
class Request {
constructor(url) {
this.url = url;
}
clone() {}
makeRequest() {}
}
// Concrete Prototype
class GetRequest extends Request {
constructor(url) {
super(url);
}
clone() {
return new GetRequest(this.url);
}
makeRequest() {
return fetch(this.url).then((response) => response.json())
}
}
class PostRequest extends Request {
constructor(url, body) {
super(url);
this.body = body;
}
clone() {
return new PostRequest(this.url, this.body);
}
makeRequest() {
return fetch(this.url, {
method: 'POST',
headers: { 'Content-Type': 'application/json'},
body: JSON.stringify(this.body)
}).then((response) => response.json())
}
}
// Usage
let getRequest = new GetRequest("https://reqres.in/api/users");
let postRequest = new PostRequest("https://reqres.in/api/users", { "name": "morpheus", "job": "leader" });
let requests = [getRequest, postRequest, getRequest.clone(), postRequest.clone()];
requests.forEach(request => {
request.makeRequest();
});
The Request class is a prototype interface, which defines the constructor and the clone() and makeRequest() methods. The GetRequest and PostRequest classes are concrete prototypes, which extend the Request class and implement their own clone() and makeRequest() methods.
In the GetRequest class, the clone() method returns a new instance of GetRequest , with the same url as the current instance. The makeRequest() method uses the fetch() function to perform a GET request to the specified url and returns the response in JSON format.
In the PostRequest class, the clone() method returns a new instance of PostRequest , with the same url and body as the current instance. The makeRequest() method uses the fetch() function to perform a POST request to the specified url, with the body specified in the constructor, and returns the response in JSON format.
At the end of the code, an instance of GetRequest and another of PostRequest is created. Next, a requests array is created and the original instances and their copies are added. Finally, the makeRequest() method is called for all instances in the requests array.
The Prototype pattern is useful when you need to create new objects from existing objects, without having to specify their class or type. Some situations where the Prototype pattern can be applied include:
In general, the Prototype pattern is a good choice when you need to create objects flexibly and efficiently without having to worry about the details of their implementation.
Conclusion
The Prototype design pattern is a creation pattern that allows you to create new objects from existing objects without having to specify their class or type. This is done by implementing a cloning method that creates a copy of the original object. It is useful when creating new objects is costly or time-consuming, when objects need to be dynamically modified but still need to maintain their basic structure, when you need to create multiple instances of an object with small variations, or when you need to avoid using multiple constructors or static methods to create different types of objects.
Hope this helps, until next time.