NashTech Insights

Understanding Popular Design Patterns in JavaScript: Singleton, Observer, and Factory

Alka Vats
Alka Vats
Table of Contents

Design patterns are reusable solutions to common software design problems that help developers create code that is more maintainable, scalable, and flexible. In JavaScript, there are several popular design patterns, each serving a specific purpose. In this blog, we will explore three widely used design patterns: Singleton, Observer, and Factory. We’ll delve into each pattern, understand its concept, and provide practical examples to illustrate how they can be implemented in JavaScript.

If you want to learn about the comparison between typescript and javascript, you can refer here.

1. Singleton Design Pattern

The Singleton pattern ensures that a class has only one instance and provides a global point of access to that instance. This is useful when you want to limit the number of instances of a particular class. It ensures that all parts of your application share the same instance.

Example:

So, Let’s say we want to create a configuration manager that reads a configuration file and provides access to its settings throughout the application.

class ConfigurationManager {
  constructor() {
    if (ConfigurationManager.instance) {
      return ConfigurationManager.instance;
    }

    // Your configuration loading logic here
    this.config = { /* configuration settings */ };

    ConfigurationManager.instance = this;
  }

  getConfig(key) {
    return this.config[key];
  }
}

// Usage
const configManager1 = new ConfigurationManager();
const configManager2 = new ConfigurationManager();

console.log(configManager1 === configManager2); // true (both instances refer to the same object)

console.log(configManager1.getConfig('apiUrl')); // Accessing configuration settings

In the example above, we create a ConfigurationManager class that has a private static property instance. When creating a new instance the ConfigurationManager, the constructor checks whether an instance already exists, and if so, it returns the existing instance. This ensures that there is only one instance of the ConfigurationManager class across the entire application.

2. Observer Design Pattern

The Observer pattern is used to establish a one-to-many dependency between objects, where one object (the subject) notifies multiple observer objects of any state changes it undergoes.

It allows objects to be loosely coupled, as the subject doesn’t need to know anything about its observers. New observers can be added without modifying the subject.

Example:

So, Let’s create a simple example of a news agency that broadcasts breaking news to multiple subscribers.

class NewsAgency {
  constructor() {
    this.subscribers = [];
  }

  subscribe(observer) {
    this.subscribers.push(observer);
  }

  unsubscribe(observer) {
    this.subscribers = this.subscribers.filter((sub) => sub !== observer);
  }

  notify(news) {
    this.subscribers.forEach((sub) => sub.update(news));
  }
}

class Subscriber {
  constructor(name) {
    this.name = name;
  }

  update(news) {
    console.log(`${this.name} received breaking news: ${news}`);
  }
}

// Usage
const newsAgency = new NewsAgency();

const subscriber1 = new Subscriber('Subscriber 1');
const subscriber2 = new Subscriber('Subscriber 2');

newsAgency.subscribe(subscriber1);
newsAgency.subscribe(subscriber2);

newsAgency.notify('Earthquake reported in the city!');

// Output:
// Subscriber 1 received breaking news: Earthquake reported in the city!
// Subscriber 2 received breaking news: Earthquake reported in the city!

In the example above, we have a NewsAgency the class that maintains a list of subscribers. The subscribe method adds new subscribers to the list, and the unsubscribe method removes subscribers and the notify method broadcasts news to all the subscribers by calling their update method. This way, NewsAgency and Subscriber classes are loosely coupled, and we can add or remove subscribers without modifying the NewsAgency class.

3. Factory Design Pattern

The Factory pattern is a creational pattern that provides an interface for creating objects but allows subclasses to alter the type of objects that will be created.

It is useful when there is a need for a centralized place to create objects, and the exact type of object is not known until runtime.

Example:

So, Let’s create a simple factory for creating different types of pizzas.

class Pizza {
  constructor(name, toppings) {
    this.name = name;
    this.toppings = toppings;
  }

  describe() {
    console.log(`Pizza: ${this.name}, Toppings: ${this.toppings.join(', ')}`);
  }
}

class PizzaFactory {
  createPizza(type) {
    switch (type) {
      case 'cheese':
        return new Pizza('Cheese Pizza', ['cheese']);
      case 'pepperoni':
        return new Pizza('Pepperoni Pizza', ['cheese', 'pepperoni']);
      case 'veggie':
        return new Pizza('Veggie Pizza', ['cheese', 'peppers', 'onions', 'olives']);
      default:
        throw new Error('Invalid pizza type');
    }
  }
}

// Usage
const pizzaFactory = new PizzaFactory();

const cheesePizza = pizzaFactory.createPizza('cheese');
const pepperoniPizza = pizzaFactory.createPizza('pepperoni');
const veggiePizza = pizzaFactory.createPizza('veggie');

cheesePizza.describe();
pepperoniPizza.describe();
veggiePizza.describe();

// Output:
// Pizza: Cheese Pizza, Toppings: cheese
// Pizza: Pepperoni Pizza, Toppings: cheese, pepperoni
// Pizza: Veggie Pizza, Toppings: cheese, peppers, onions, olives

In the example above, we have a Pizza class representing different types of pizzas. The PizzaFactory class is responsible for creating pizzas based on the given type. So, It encapsulates the creation logic, allowing the client code to create pizzas.

Conclusion

Understanding and utilizing design patterns is crucial for writing maintainable and efficient code. In this blog, we covered three popular design patterns in JavaScript: Singleton, Observer, and Factory. Hence, Each pattern serves its purpose and can greatly improve the structure and organization of your code. By incorporating these design patterns into your projects, you can enhance code reusability, maintainability, and overall software design.

Finally, for more such posts, please follow our LinkedIn page- FrontEnd Competency.

Alka Vats

Alka Vats

Alka Vats is a Software Consultant at Nashtech. She is passionate about web development. She is recognized as a good team player, a dedicated and responsible professional, and a technology enthusiast. She is a quick learner & curious to learn new technologies. Her hobbies include reading books, watching movies, and traveling.

Leave a Comment

Your email address will not be published. Required fields are marked *

Suggested Article

%d bloggers like this: