Design patterns are essential in software development, offering reusable solutions to common problems. In this article, we'll explore how to implement design patterns using JavaScript classes. This approach helps in writing clean, maintainable, and efficient code. JavaScript classes, introduced in ECMAScript 6, provide a blueprint for creating objects and encapsulating functionality and state.
Understanding JavaScript Classes
JavaScript classes are syntactic sugar over the prototypical inheritance. They enable you to create object templates with constructors and methods conveniently.
class Car {
constructor(brand) {
this.brand = brand;
}
showBrand() {
console.log(this.brand);
}
}
const myCar = new Car('Toyota');
myCar.showBrand(); // Toyota
Creational Pattern: Singleton
The Singleton pattern restricts class instantiation to one object. It ensures that a class has only one instance and provides a global point of access to it.
class Singleton {
constructor() {
if (!Singleton.instance) {
Singleton.instance = this;
}
return Singleton.instance;
}
show() {
console.log("Singleton instance!");
}
}
const singleton1 = new Singleton();
const singleton2 = new Singleton();
singleton1.show();
console.log(singleton1 === singleton2); // true
In this example, any number of instances created will refer back to the same one, preserving the singleton nature.
Structural Pattern: Decorator
The Decorator pattern allows adding new behaviors to objects without modifying their structure. It is especially useful when the functionality must change dynamically.
class Coffee {
cost() {
return 5;
}
}
class MilkDecorator {
constructor(coffee) {
this.coffee = coffee;
}
cost() {
return this.coffee.cost() + 1.5;
}
}
let coffee = new Coffee();
coffee = new MilkDecorator(coffee);
console.log(coffee.cost()); // 6.5
Here, the initial cost of the coffee is incremented by wrapping it in a Decorator class.
Behavioral Pattern: Observer
The Observer pattern is used to define a one-to-many dependency between objects. When one object's state changes, all its dependents are notified automatically.
class Subject {
constructor() {
this.observers = [];
}
subscribe(observer) {
this.observers.push(observer);
}
unsubscribe(observer) {
this.observers = this.observers.filter(obs => obs !== observer);
}
notify(data) {
this.observers.forEach(observer => observer.update(data));
}
}
class Observer {
update(data) {
console.log('Data: ', data);
}
}
const subject = new Subject();
const observer1 = new Observer();
subject.subscribe(observer1);
subject.notify('Pattern implemented!'); // Observer gets notified with the data
The Subject class maintains a list of observers and handles notifying them of changes.
Implementing these design patterns using JavaScript classes enhances code readability and reusability. Each pattern provides a unique mechanism to solve specific design goals. Understanding and applying these patterns effectively aids in building robust applications.