Builder Pattern
Introduction
The Builder pattern is a creational design pattern that separates the construction of a complex object from its representation. This pattern is particularly useful when an object needs to be created with many possible configurations and constructing such an object is a complex process.
Purpose
- To separate the construction of a complex object from its representation.
- To allow the same construction process to create various representations.
Use Cases
- When the algorithm for creating a complex object should be independent of the parts that make up the object and how they’re assembled.
- When the construction process must allow different representations for the object that’s constructed.
Advantages/Disadvantages
Advantages:
- Provides control over the construction process.
- Supports varying the internal representation of products.
- Isolates complex construction code from the business logic of the product.
Disadvantages:
- Can increase the overall complexity of the code by adding multiple additional classes.
Implementation in JavaScript
class Builder {
buildPartA() {}
buildPartB() {}
getResult() {}
}
class ConcreteBuilder extends Builder {
constructor() {
super();
this.product = new Product();
}
buildPartA() {
this.product.add('PartA');
}
buildPartB() {
this.product.add('PartB');
}
getResult() {
return this.product;
}
}
class Product {
constructor() {
this.parts = [];
}
add(part) {
this.parts.push(part);
}
}
// Usage
const builder = new ConcreteBuilder();
builder.buildPartA();
builder.buildPartB();
const product = builder.getResult();
console.log(product); // Product with parts ['PartA', 'PartB']
Practical Example
Imagine we’re building a system for generating reports in different formats (e.g., JSON, XML). We’ll use the Builder pattern to construct various types of reports.
class ReportBuilder {
setHeader() {}
setContent() {}
setFooter() {}
getReport() {}
}
class JSONReportBuilder extends ReportBuilder {
constructor() {
super();
this.report = { header: '', content: '', footer: '' };
}
setHeader(header) {
this.report.header = header;
}
setContent(content) {
this.report.content = content;
}
setFooter(footer) {
this.report.footer = footer;
}
getReport() {
return JSON.stringify(this.report);
}
}
class XMLReportBuilder extends ReportBuilder {
constructor() {
super();
this.report = '<report>';
}
setHeader(header) {
this.report += `<header>${header}</header>`;
}
setContent(content) {
this.report += `<content>${content}</content>`;
}
setFooter(footer) {
this.report += `<footer>${footer}</footer>`;
}
getReport() {
return this.report + '</report>';
}
}
// Usage
const jsonBuilder = new JSONReportBuilder();
jsonBuilder.setHeader('JSON Report Header');
jsonBuilder.setContent('This is the content');
jsonBuilder.setFooter('JSON Report Footer');
const jsonReport = jsonBuilder.getReport();
const xmlBuilder = new XMLReportBuilder();
xmlBuilder.setHeader('XML Report Header');
xmlBuilder.setContent('This is the content');
xmlBuilder.setFooter('XML Report Footer');
const xmlReport = xmlBuilder.getReport();
console.log(jsonReport); // JSON formatted report
console.log(xmlReport); // XML formatted report
In this practical example, the JSONReportBuilder
and XMLReportBuilder
classes implement the ReportBuilder
interface to build reports in JSON and XML formats, respectively. This demonstrates how the Builder pattern can be used to construct complex objects step-by-step.