Skip to main content

Real-World Examples (Live Playground)

In this tutorial, we'll analyze real-world examples of JavaScript design patterns, showcasing their practical applications and best practices. By understanding these patterns, you can enhance your JavaScript skills and create more efficient, maintainable code.

Real-World Example: Singleton Pattern

The Singleton Pattern is used to restrict the instantiation of a class to a single instance. In web development, you might use this pattern for managing application state, configuration data, or logging.

Example: Simple Logger using Singleton Pattern

TypeScript
class Logger {
constructor() {
if (Logger.instance) {
return Logger.instance;
}

Logger.instance = this;
}

log(message) {
console.log(message);
}
}

const logger1 = new Logger();
const logger2 = new Logger();

console.log(logger1 === logger2); // Output: true

In this example, we create a simple Logger class that uses the Singleton Pattern to ensure only one instance is created. If you try to create multiple instances of Logger, you'll always get the same instance.

Live Playground, Try it Yourself

Real-World Example: Observer Pattern

The Observer Pattern is used to create a one-to-many dependency between objects, allowing one object to notify multiple observers when its state changes. In web development, you might use this pattern for implementing event systems, data binding, or state management libraries.

Example: Simple Event Emitter using Observer Pattern

TypeScript
class EventEmitter {
constructor() {
this.listeners = {};
}

on(eventName, callback) {
if (!this.listeners[eventName]) {
this.listeners[eventName] = [];
}
this.listeners[eventName].push(callback);
}

emit(eventName, ...args) {
if (this.listeners[eventName]) {
this.listeners[eventName].forEach(callback => callback(...args));
}
}
}

const eventEmitter = new EventEmitter();

eventEmitter.on('dataReceived', data => console.log('Data received:', data));
eventEmitter.on('dataReceived', data => console.log('Another listener:', data));

eventEmitter.emit('dataReceived', 'Sample data');

In this example, we create a simple EventEmitter class that uses the Observer Pattern to manage event listeners and emit events. The EventEmitter maintains a list of listeners for each event and invokes them when the corresponding event is emitted.

Live Playground, Try it Yourself

Real-World Example: Module Pattern

The Module Pattern is used to encapsulate functionality and create private scope, preventing global namespace pollution. In web development, you might use this pattern for organizing code, managing dependencies, or encapsulating functionality.

Example: Simple Calculator using Module Pattern

TypeScript
const calculator = (() => {
const add = (a, b) => a + b;
const subtract = (a, b) => a - b;
const multiply = (a, b) => a * b;
const divide = (a, b) => a / b;

return {
add,
subtract,
multiply,
divide,
};
})();

console.log(calculator.add(2, 3)); // Output: 5
console.log(calculator.subtract(7, 3)); // Output: 4

In this example, we create a simple calculator object using the Module Pattern. By using an Immediately Invoked Function Expression (IIFE), we encapsulate the functionality within a private scope and expose only the methods we want to make public.

Live Playground, Try it Yourself

Real-World Example: Factory Pattern

The Factory Pattern is used to create objects without specifying the exact class of the object that will be created. In web development, you might use this pattern for creating and managing various types of objects, such as UI components or data models, without tightly coupling them to specific classes.

Example: Simple Shape Factory

TypeScript
class Circle {
constructor(radius) {
this.radius = radius;
}

getArea() {
return Math.PI * this.radius * this.radius;
}
}

class Square {
constructor(side) {
this.side = side;
}

getArea() {
return this.side * this.side;
}
}

function shapeFactory(type, ...args) {
switch (type) {
case 'circle':
return new Circle(...args);
case 'square':
return new Square(...args);
default:
throw new Error('Invalid shape type');
}
}

const circle = shapeFactory('circle', 5);
const square = shapeFactory('square', 4);

console.log(circle.getArea()); // Output: 78.53981633974483
console.log(square.getArea()); // Output: 16

In this example, we create a simple shapeFactory function that uses the Factory Pattern to create Circle and Square objects. The factory function takes a type argument and additional parameters to create the appropriate shape based on the input.

Live Playground, Try it Yourself

Conclusion

By understanding real-world applications of JavaScript design patterns and following best practices, you can create more maintainable, efficient, and modular code. By incorporating these examples into your projects, you'll be better equipped to tackle complex web development challenges and build robust applications.