Scalable Business for Startups

Get the oars in the water and start rowing. Execution is the single biggest factor in achievement so the faster and better your execution.

+91 9472774549 thebytemind@gmail.com Looking for collaboration for your next creative project?

Chapter 19: JavaScript Design Patterns

Introduction to Design Patterns
  • Definition of Design Patterns
    • A design pattern is a reusable solution to a commonly occurring problem within a given context in software design.
  • Importance of Design Patterns
    • Provides standardized methods for solving issues.
    • Enhances code readability and maintainability.
    • Facilitates communication among developers.
  • Categories of Design Patterns
    • Creational Patterns
    • Structural Patterns
    • Behavioral Patterns

Section 1: Creational Patterns

Creational patterns deal with object creation mechanisms, trying to create objects in a manner suitable to the situation.

1.1 Singleton Pattern
  • Description
    • Ensures a class has only one instance and provides a global point of access to it.
  • Implementation Example
    class Singleton {
        constructor() {
            if (Singleton.instance) {
                return Singleton.instance;
            }
            Singleton.instance = this;
            this.timestamp = new Date();
        }
    }
    
    const instance1 = new Singleton();
    const instance2 = new Singleton();
    
    console.log(instance1 === instance2); // true
    console.log(instance1.timestamp); // shows the same timestamp for both instances
            
1.2 Factory Pattern
  • Description
    • Provides an interface for creating objects in a superclass but allows subclasses to alter the type of objects that will be created.
  • Implementation Example
    class Car {
        constructor(make, model) {
            this.make = make;
            this.model = model;
        }
    }
    
    class CarFactory {
        static createCar(make, model) {
            return new Car(make, model);
        }
    }
    
    const car1 = CarFactory.createCar('Toyota', 'Corolla');
    console.log(car1); // Car { make: 'Toyota', model: 'Corolla' }
            
1.3 Constructor Pattern
  • Description
    • Uses a constructor function to create an object and return the instance of the object.
  • Implementation Example
    function Car(make, model) {
        this.make = make;
        this.model = model;
    }
    
    const myCar = new Car('Honda', 'Civic');
    console.log(myCar); // Car { make: 'Honda', model: 'Civic' }
            

Section 2: Structural Patterns

Structural patterns deal with object composition and help ensure that if one part of a system changes, the entire system doesn't need to change.

2.1 Adapter Pattern
  • Description
    • Allows incompatible interfaces to work together by converting the interface of a class into another interface clients expect.
  • Implementation Example
    class OldSystem {
        request() {
            return 'Request from Old System';
        }
    }
    
    class NewSystem {
        specificRequest() {
            return 'Request from New System';
        }
    }
    
    class Adapter {
        constructor(newSystem) {
            this.newSystem = newSystem;
        }
    
        request() {
            return this.newSystem.specificRequest();
        }
    }
    
    const oldSystem = new OldSystem();
    const newSystem = new NewSystem();
    const adapter = new Adapter(newSystem);
    
    console.log(oldSystem.request()); // Request from Old System
    console.log(adapter.request()); // Request from New System
            
2.2 Decorator Pattern
  • Description
    • Allows behavior to be added to individual objects, either statically or dynamically, without affecting the behavior of other objects from the same class.
  • Implementation Example
    class Coffee {
        cost() {
            return 5;
        }
    }
    
    class MilkDecorator {
        constructor(coffee) {
            this.coffee = coffee;
        }
    
        cost() {
            return this.coffee.cost() + 1;
        }
    }
    
    class SugarDecorator {
        constructor(coffee) {
            this.coffee = coffee;
        }
    
        cost() {
            return this.coffee.cost() + 0.5;
        }
    }
    
    const coffee = new Coffee();
    const milkCoffee = new MilkDecorator(coffee);
    const milkSugarCoffee = new SugarDecorator(milkCoffee);
    
    console.log(milkSugarCoffee.cost()); // 6.5
            

Section 3: Behavioral Patterns

Behavioral patterns focus on communication between objects, what goes on between objects, and how they communicate.

3.1 Observer Pattern
  • Description
    • Defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
  • Implementation Example
    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(`Observer received data: ${data}`);
        }
    }
    
    const subject = new Subject();
    const observer1 = new Observer();
    const observer2 = new Observer();
    
    subject.subscribe(observer1);
    subject.subscribe(observer2);
    
    subject.notify('New Data'); // Both observers receive the data
            
3.2 Command Pattern
  • Description
    • Encapsulates a request as an object, thereby allowing for parameterization of clients with queues, requests, and operations.
  • Implementation Example
    class Command {
        execute() {
            console.log('Executing command');
        }
    }
    
    class CommandInvoker {
        constructor() {
            this.commands = [];
        }
    
        storeCommand(command) {
            this.commands.push(command);
        }
    
        executeCommands() {
            this.commands.forEach(command => command.execute());
        }
    }
    
    const command1 = new Command();
    const invoker = new CommandInvoker();
    
    invoker.storeCommand(command1);
    invoker.executeCommands(); // Executing command
            

Conclusion

  • Summary of Design Patterns
    • Reiterate the significance of design patterns in JavaScript development.
    • Emphasize their role in improving code quality and facilitating better collaboration among developers.
  • Encouragement to Explore Further
    • Suggest resources for further learning.
    • Encourage the reader to apply these patterns in real-world projects for better understanding.