Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases now! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
JavaScript Design Patterns

You're reading from   JavaScript Design Patterns Deliver fast and efficient production-grade JavaScript applications at scale

Arrow left icon
Product type Paperback
Published in Mar 2024
Publisher Packt
ISBN-13 9781804612279
Length 308 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Hugo Di Francesco Hugo Di Francesco
Author Profile Icon Hugo Di Francesco
Hugo Di Francesco
Arrow right icon
View More author details
Toc

Table of Contents (16) Chapters Close

Preface 1. Part 1:Design Patterns
2. Chapter 1: Working with Creational Design Patterns FREE CHAPTER 3. Chapter 2: Implementing Structural Design Patterns 4. Chapter 3: Leveraging Behavioral Design Patterns 5. Part 2:Architecture and UI Patterns
6. Chapter 4: Exploring Reactive View Library Patterns 7. Chapter 5: Rendering Strategies and Page Hydration 8. Chapter 6: Micro Frontends, Zones, and Islands Architectures 9. Part 3:Performance and Security Patterns
10. Chapter 7: Asynchronous Programming Performance Patterns 11. Chapter 8: Event-Driven Programming Patterns 12. Chapter 9: Maximizing Performance – Lazy Loading and Code Splitting 13. Chapter 10: Asset Loading Strategies and Executing Code off the Main Thread 14. Index 15. Other Books You May Enjoy

The factory pattern in JavaScript

In a similar fashion to the discussion about the JavaScript “prototype” versus the prototype creational design pattern, “factory” refers to related but different concepts when it comes to general program design discussions and design patterns.

A “factory,” in the general programming sense, is an object that’s built with the goal of creating other objects. This is hinted at by the name that refers to a facility that processes items from one shape into another (or from one type of item to another). This factory denomination means that the output of a function or method is a new object. In JavaScript, this means that something as simple as a function that returns an object literal is a factory function:

const simpleFactoryFunction = () => ({}); // returns an object, therefore it's a factory.

This definition of a factory is useful, but this section of the chapter is about the factory design pattern, which does fit into this overall “factory” definition.

The factory or factory method design pattern solves a class inheritance problem. A base or superclass is extended (the extended class is a subclass). The base class’s role is to provide orchestration for the methods implemented in the subclasses, as we want the subclasses to control which other objects to populate an instance with.

Implementation

A factory example is as follows. We have a Building base class that implements a generateBuilding() method. For now, it’s going to create a top floor using the makeTopFloor instance method. In the base class (Building), makeTopFloor is implemented, mainly because JavaScript doesn’t provide a way to define abstract methods. The makeTopFloor implementation throws an error because subclasses should override it; makeTopFloor is the “factory method” in this case. It’s how the base class defers the instantiation of objects to the subclasses:

class Building {
  generateBuilding() {
    this.topFloor = this.makeTopFloor();
  }
  makeTopFloor() {
    throw new Error('not implemented, left for subclasses
      to implement');
  }
}

If we wanted to implement a single-story house, we would extend Building and override makeTopFloor; in this instance, topFloor will have level: 1.

class House extends Building {
  makeTopFloor() {
    return {
      level: 1,
    };
  }
}

When we instantiate House, which is a subclass of Building, we have access to the generateBuilding method; when called, it sets topFloor correctly (to { level: 1 }).

const house = new House();
house.generateBuilding();
console.assert(house.topFloor.level === 1, 'topFloor works
  in House');

Now, if we want to create a different type of building that has a very different top floor, we can still extend Building; we simply override makeTopFloor to return a different floor. In the case of a skyscraper, we want the top floor to be very high, so we’ll do the following:

class SkyScraper extends Building {
  makeTopFloor() {
    return {
      level: 125,
    };
  }
}

Having defined our SkyScraper, which is a subclass of Building, we can instantiate it and call generateBuilding. As in the preceding House case, the generateBuilding method will use SkyScraper’s makeTopFloor method to populate the topFloor instance property:

const skyScraper = new SkyScraper();
skyScraper.generateBuilding();
console.assert(skyScraper.topFloor.level > 100, 'topFloor
  works in SkyScraper');

The “factory method” in this case is makeTopFloor. The makeTopFloor method is “not implemented” in the base class, in the sense that it’s implemented in a manner that forces subclasses that wish to use generateBuilding to define a makeTopFloor override.

Note that makeTopFloor in our examples returned object literals, as mentioned earlier in the chapter; this is a feature of JavaScript not available in all object-oriented languages (JavaScript is multi-paradigm). We’ll see different ways to implement the factory pattern later in this section.

Use cases

The benefit of using a factory method is that we can create a wide variety of subclasses without modifying the base class. This is the “open/closed principle” at play – the Building class in our example is “open” to extension (i.e., can be subclassed to infinity for different types of buildings) but “closed” to modification (i.e., we don’t need to make changes in Building for every subclass, only when we want to add new behaviors).

Improvements with modern JavaScript

The key improvement we can make with JavaScript is enabled by its first-class support for functions and the ability to define objects using literals (instead of classes being instantiated).

JavaScript having “first-class functions” means functions are like any other type – they can be passed as parameters, set as variable values, and returned from other functions.

A more idiomatic implementation of this pattern would probably involve a generateBuilding standalone function instead of a Building class. generateBuilding would take makeTopFloor either as a parameter or take an object parameter with a makeTopFloor key. The output of generateBuilding would be an object created using an object literal, which takes the output of makeTopFloor() and sets it as the value to a topFloor key:

function generateBuilding({ makeTopFloor }) {
  return {
    topFloor: makeTopFloor(),
  };
}

In order to create our house and skyscraper, we would call generateBuilding with the relevant makeTopFloor functions. In the case of the house, we want a top floor that is on level 1; in the case of the skyscraper, we want a top floor on level 125.

const house = generateBuilding({
  makeTopFloor() {
    return {
      level: 1,
    };
  },
});
console.assert(house.topFloor.level === 1, 'topFloor works
  in house');
const skyScraper = generateBuilding({
  makeTopFloor() {
    return {
      level: 125,
    };
  },
});
console.assert(skyScraper.topFloor.level > 100, 'topFloor works in skyScraper');

One reason why using functions directly works better in JavaScript is that we didn’t have to implement a “throw an error to remind consumers to override me” makeFloor method that we had with the Building class.

In languages other than JavaScript that have support for abstract methods, this pattern is more useful and natural to implement than in JavaScript, where we have first-class functions.

You also have to bear in mind that the original versions of JavaScript/ECMAScript didn’t include a class construct.

In the final section of the chapter, we learned what the factory method pattern is and how it contrasts with the factory programming concept. We then implemented a class-based factory pattern scenario as well as a more idiomatic JavaScript version. Interspersed through this section, we covered the use cases, benefits, and drawbacks of the factory method pattern in JavaScript.

You have been reading a chapter from
JavaScript Design Patterns
Published in: Mar 2024
Publisher: Packt
ISBN-13: 9781804612279
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime