Posted January 25Jan 25 You are reading Part 30 of the 39-part series: JavaScript Skill Progression: The Path from Beginner to Extreme. [Level 5]IntroductionDesign patterns are common solutions to recurring problems in software development. In JavaScript, these patterns help improve code organization, maintainability, and scalability. This article explores five important design patterns: Singleton, Factory, Observer, Module, and Proxy.1. Singleton PatternThe Singleton Pattern ensures that only one instance of an object exists and provides a global access point to it. This pattern is useful for managing shared resources, such as database connections or application configurations.Example: Singleton Implementationclass Singleton { constructor() { if (!Singleton.instance) { Singleton.instance = this; } return Singleton.instance; } logMessage() { console.log("Singleton Instance Active"); } } const instance1 = new Singleton(); const instance2 = new Singleton(); console.log(instance1 === instance2); // Output: true Ensures only one instance of Singleton exists.The second instance returns the same reference as the first.2. Factory PatternThe Factory Pattern provides a way to create objects without specifying the exact class of object that will be created. It helps manage complex object creation logic.Example: Factory Functionclass Car { constructor(make, model) { this.make = make; this.model = model; } } class CarFactory { createCar(make, model) { return new Car(make, model); } } const factory = new CarFactory(); const car1 = factory.createCar("Tesla", "Model S"); const car2 = factory.createCar("Ford", "Mustang"); console.log(car1, car2); Abstracts object creation, making it flexible and reusable.3. Observer PatternThe Observer Pattern allows an object (the subject) to maintain a list of dependent objects (observers) that need to be notified when the subject changes.Example: Implementing an Event Emitter (Observer Pattern)class EventEmitter { constructor() { this.events = {}; } on(event, listener) { if (!this.events[event]) { this.events[event] = []; } this.events[event].push(listener); } emit(event, data) { if (this.events[event]) { this.events[event].forEach(listener => listener(data)); } } } const emitter = new EventEmitter(); emitter.on("message", data => console.log("Received:", data)); emitter.emit("message", "Hello Observer Pattern"); Useful for implementing event-driven architectures like Pub-Sub systems.4. Module PatternThe Module Pattern encapsulates functionality in an independent, reusable unit, avoiding polluting the global scope.Example: Module Pattern Using an IIFE (Immediately Invoked Function Expression)const CounterModule = (function() { let count = 0; return { increment: function() { count++; console.log("Count:", count); }, getCount: function() { return count; } }; })(); CounterModule.increment(); // Output: Count: 1 console.log(CounterModule.getCount()); // Output: 1 Prevents direct access to internal variables (encapsulation).Encourages modular development.5. Proxy PatternThe Proxy Pattern provides an intermediary to control access to an object. This is useful for validation, logging, caching, and lazy loading.Example: Proxy for Validationconst user = { name: "John Doe", age: 25 }; const userProxy = new Proxy(user, { get(target, property) { console.log(`Accessing ${property}:`, target[property]); return target[property]; }, set(target, property, value) { if (property === "age" && value < 0) { throw new Error("Age cannot be negative"); } target[property] = value; console.log(`${property} updated to ${value}`); return true; } }); console.log(userProxy.name); userProxy.age = 30; // userProxy.age = -5; // Throws error Allows control over property access and mutation.Useful for security checks, API rate limiting, and caching.You are reading Part 30 of the 39-part series: JavaScript Skill Progression: The Path from Beginner to Extreme. [Level 5]
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.