Understanding the Basics of Structural Design Patterns in JavaScript

Introduction

Structural design patterns are used to solve problems related to the composition of objects and their relationships. They help to organize code into larger structures, making it easier to maintain and modify.

Concerned with how objects are made up and simplify relationships between objects.

The four most common structural design patterns in JavaScript are:

  1. Adapter Pattern: The Adapter pattern allows objects with incompatible interfaces to work together. It acts as a bridge between two incompatible interfaces, making them compatible.
  2. Decorator Pattern: The Decorator pattern adds functionality to an object dynamically without changing its original structure. It is used to extend the functionality of an object at runtime.
  3. Facade Pattern: The Facade pattern provides a simplified interface to a complex system. It acts as a high-level interface that makes it easier to use a complex system by providing a simplified interface to it.
  4. Flyweight Pattern: The Flyweight Pattern is useful when an application needs to create a large number of similar objects that differ only in some small aspects. By sharing the common properties, it reduces the memory footprint of an application and improves performance.

Adapter Pattern

The Adapter Pattern is a structural design pattern that allows objects with incompatible interfaces to work together by creating an adapter that acts as an intermediary between the two objects. The adapter translates the interface of one object into an interface that the other object can understand.

Adapter Pattern introduces an intermediary piece of code that makes two parts of a system compatible with one another.

Let’s say we have an existing class that provides a certain interface, but we want to use that class in a new context where it needs to conform to a different interface. We can use the Adapter Pattern to create a new class that adapts the existing class to the new interface.

Now understand Adapter Pattern with an example and here we are going to explain an example in steps:

Building Better Objects with Creational Design Patterns in JavaScript

As a JavaScript developer, you know that creating and managing objects can be a challenging task, especially as your projects grow in complexity. That’s where creational design patterns come in – they provide a proven blueprint for object creation that can help you write more efficient, scalable, and maintainable code.

In this blog post, we’ll explore the world of creational design patterns in JavaScript and discover how they can help us build better objects. We’ll start by introducing the basics of creational design patterns, discussing their advantages and disadvantages, and exploring some common patterns like the factory pattern, the builder pattern, and the singleton pattern.

By the end of this post, we’ll have a solid understanding of how to use creational design patterns to create more flexible and reusable objects, improve the performance and reliability of our code, and take our JavaScript skills to the next level. So, let’s get started!

In this post, we are going to start with Factory Pattern then after we will cover other patterns one by one :

Factory Pattern

The Factory Pattern is a creational design pattern that provides an interface for creating objects but allows subclasses to alter the type of objects that will be created. This pattern is often used when you need to create a large number of similar objects, or when the process of creating an object is complex and involves a lot of boilerplate code.

Factory Pattern is used to simplify Object Creation.

In JavaScript, the Factory Pattern is typically implemented using a factory function, which is a function that returns an object. The factory function can take arguments and use them to customize the object that it creates.

How to Create Factory Pattern in Javascript?

To Create a Factory Pattern in Javascript we need to follow the following steps:

Step 1: Creating a Factory Function

To create a factory function, we’ll need to define a function that returns an object.

Here’s an example:


  function createCar(make, model, year) {
    return {
      make: make,
      model: model,
      year: year,
      drive: function() {
        console.log(`Driving the ${this.make} ${this.model}...`);
      }
    };
  }
  

In this example, the createCar function takes three arguments: make, model, and year. It then returns an object that has properties for make, model, and year, as well as a drive method that logs a message to the console.

Step 2: Implementing the Factory Function

To use the factory function, we’ll need to call it and pass in any arguments that it requires.

Here’s an example:


const car1 = createCar('Honda', 'Civic', 2022);
const car2 = createCar('Toyota', 'Corolla', 2022);
  

In this example, we’re calling the createCar function twice, passing in different arguments each time. This will create two separate objects, one for a Honda Civic and one for a Toyota Corolla.

Step 3: Using the Factory Function

Once we’ve created the objects using the factory function, we can use them just like any other object in JavaScript.

Here’s an example:


  car1.drive(); // logs "Driving the Honda Civic..."
  car2.drive(); // logs "Driving the Toyota Corolla..."
  

In this example, we’re calling the drive method on each of the objects created by the factory function. This will log a message to the console indicating that we’re driving the corresponding car.

Pros and Cons of Factory Pattern in Javascript

Pros

  1. Encapsulation: The Factory Pattern encapsulates object creation and allows you to create objects without exposing the creation logic. This helps to keep your code organized and easy to maintain.
  2. Abstraction: The Factory Pattern provides an abstraction layer between the object creation and the client code, making it easy to change the object creation process without affecting the client code.
  3. Flexibility: With the Factory Pattern, you can create objects dynamically at runtime, based on user input or other conditions. This makes it a flexible and versatile pattern for object creation.
  4. Reusability: The Factory Pattern promotes code reusability by providing a centralized place for object creation logic. This reduces code duplication and makes it easier to maintain and modify the code.

Cons

  1. Complexity: The Factory Pattern can add some complexity to your code, especially if you need to create many different types of objects. It can also be difficult to understand and implement for beginners.
  2. Performance: The Factory Pattern can have a negative impact on performance if the factory function is called frequently or if the object creation process is resource-intensive.
  3. Coupling: The Factory Pattern can create tight coupling between the factory and the created objects, which can make it difficult to change the object creation process later.

Singleton Pattern

The Singleton Pattern is a design pattern that restricts the instantiation of a class to a single instance and provides a global point of access to that instance. In JavaScript, you can implement the Singleton Pattern using a simple object literal or a constructor function.

Here’s an example of how to create a Singleton using an object literal:


  const singleton = {
    instance: null,
    getInstance: function() {
      if (!this.instance) {
        this.instance = { 
          // properties and methods of your singleton object
        };
      }
      return this.instance;
    }
  };
  

In this example, we define a singleton object that has an instance property and a getInstance method. The getInstance method checks whether the instance property is null, and if so, it creates a new instance of the object. If the instance property is not null, it simply returns the existing instance.

To use the Singleton, you can call the getInstance method:


  const mySingleton = singleton.getInstance();
  

This will either create a new instance of the object or return the existing instance, depending on whether an instance has already been created.

Here’s an example of how to create a Singleton using a constructor function:


  function MySingleton() {
    if (!MySingleton.instance) {
      MySingleton.instance = this;
      // properties and methods of your singleton object
    }
    return MySingleton.instance;
  }

In this example, we define a MySingleton constructor function that checks whether the instance property is null and creates a new instance of the object if it is. We also set the instance property to the current object using this, and return the instance.

To use the Singleton, you can create a new instance of the MySingleton constructor function:


  const mySingleton = new MySingleton();

This will either create a new instance of the object or return the existing instance, depending on whether an instance has already been created.

The Singleton Pattern offers several advantages, including easy access to a single instance of an object, and control over the instantiation process. However, it can also have some drawbacks, such as tight coupling between the Singleton object and other parts of your code, and difficulty in unit testing. It’s important to weigh the pros and cons of using the Singleton Pattern in your specific use case and to use it judiciously.

Pros and Cons of Singleton Pattern in Javascript

Pros

  1. Easy access to a single instance: The Singleton Pattern provides a global point of access to a single instance of an object, making it easy to access that object from anywhere in your code.
  2. Control over the instantiation process: By restricting the instantiation of a class to a single instance, the Singleton Pattern gives you control over the creation of that object and ensures that only one instance is created.
  3. Conserves resources: By limiting the number of instances of an object, the Singleton Pattern can help conserve resources in your application.
  4. Provides a shared state: Because there is only one instance of the object, it can be used to maintain a shared state across your application.

Cons

  1. Tight coupling: The Singleton Pattern can lead to tight coupling between the Singleton object and other parts of your code. This can make it difficult to change the behavior of your code without also changing the behavior of the Singleton object.
  2. Hard to unit test: Because the Singleton object is global, it can be difficult to unit test your code in isolation. This can make it harder to catch bugs and maintain your code over time.
  3. Can lead to code bloat: Because the Singleton object is global, it can be easy to add too many properties and methods to the object, leading to code bloat and decreased maintainability.
  4. Can lead to unexpected behavior: Because there is only one instance of the object, any changes made to that object will be reflected across your application. This can lead to unexpected behavior and make it harder to reason about your code.