Index Blog

Mastering Creational Design Patterns for Developers: Part 1

February 15, 2024

Mastering Creational Design Patterns for Developers: Part 1
Radu PoclitariRadu Poclitari, Copywriter

I consider design patterns as good weapons for developers’ arsenal. The more you have/know, the better.

However, you must not make the same mistake I did when I first discovered them.

If you use the wrong pattern or try to force it, it’s gonna blow up your project.

When I first read about them, I was trying to “patternize” everything and it back-fired heavily.

Knowing when to use a pattern is the hardest part about them.

You can not learn that, you have to discover that with trial and error, build the experience in order to be able to feel when a specific pattern will be a good fit and when is best to avoid it.

This article is part 1 of the design pattern series which will consist of 3 articles. Here we will focus on creational patterns:

  1. Builder
  2. Singleton
  3. Abstract Factory
  4. Factory Method
  5. Prototype (Clone)

Creational patterns provide a variety of object generation strategies, increasing flexibility and reusing existing code.

We will go through each pattern and give examples from real life followed by code implementation in TypeScript.

Calling all software engineers seeking full-time remote roles! Join Index.dev remote-work platform and secure enduring remote positions with leading tech companies →

1) Builder

Let’s start with the simple and very popular one - a builder.

This is a design pattern that lets you construct your object piece by piece. It is used a lot in situations where you need to create different types of base object.

Imagine you have a pancake shop, people can order any type of pancakes they like with different toppings.

For example, customers can add Nutella, banana, walnut, coconut, forest fruit, cinnamon, cherries, chocolate pudding, etc.

Let’s implement this in the code. For the sake of simplicity, we will only take 3 ingredients: Nutella, banana, and ice cream.

As you can see above, first, the builder class PancakeBuilder is implemented and it consists of all the steps we might need to cook a pancake with different kinds of ingredients.

For example, there is addNutella method which simply set the property value to true and returns the PancakeBuilder instance.

Plus, there is also a getter for each ingredient (eg. Nutella) which simply returns the value. Finally, there is cook method that returns a new instance of Pancake.

The Pancake class accepts the PancakeBuilder instance in the constructor and takes the values for various ingredients.

There is also an overridden toString method that prints the pancake with all the ingredients.

Let’s test this:

2) Singleton

By implementing singleton you can ensure that a specific class can only have one instance and you provide global access to that instance.

This is usually used in situations when there is a specific limit to resources.

For example, if you go to Mcdonald’s, you can see that they have a lot of self-service terminals. So customers can order any meals they want, which implies there is no limit to resources.

The opposite of that is buying tickets for movies in the cinema. Seats are a limited resource and if you picked seats while buying tickets, no one else should be able to pick your seats.

Usually, the website gives you a timer to finish your purchase, otherwise, you lose your seats.

Lets’s implement singleton:

As you can see, inside the class, you need to have a variable that acts like a placeholder, that variable is used to store singleton.

There is also a private constructor which ensures that no instances can be created.

Finally, there is the getInstance method that checks if an instance of the class already exists, and if not a new one is created.

If it exists, an existing instance is returned.

3) Abstract factory

This pattern is used to elegantly generate instances of familiar classes without having to specify their exact classes.

Sounds complicated but it’s actually very simple, so let’s explain it with a real-life example.

Let’s say you need to implement a clothing style mechanism for a webshop. To make things simple, we will use only 3 clothing styles: business, sportswear, and vintage. Each style has shoes, pants, and a shirt.

Basically, you want to produce matching clothing pieces. You don’t want to mix vintage shirts with sportswear pants.

This is where abstract factory comes in handy. So let’s implement it in the code.

For the sake of simplicity, we will only implement business and sports clothing lines with 2 products, shoes and shirts.

As you can see above, the abstract factory for this example is implemented in a couple of steps:

  • Interfaces for products are defined (Shirt and Shoes)
  • The interface for the factory is defined (ClothingFactory)
  • Classes that implement product interfaces are defined (SportShirt, SportShoes, BusinessShirt, BusinessShoes)
  • Concrete factory classes which implement factory interface are defined (SportStyleFactory, BusinessStyleFactory)
  • Finally, there is ClothingLine class which accepts ClothingFactory instance and creates products

We can test this code like this:

As you can see each clothing line produces family-specific clothing pieces.

Calling all software engineers seeking full-time remote roles! Join Index.dev remote-work platform and secure enduring remote positions with leading tech companies →

4) Factory Method

This design pattern provides an interface to create objects in the base class but it also offers flexibility for extension classes to alter the type of objects which will be created.

Let’s take a fast food restaurant with delivery as an example. They do delivery with bikes but also with motorbikes and cars.

Let’s implement that use case in the code:

As you can see there are 3 delivery types that are implemented the Delivery interface.

Next, there is a namespace FastFood with a factory method called deliverFood.

This method accepts the type of delivery and simply returns the concrete delivery type. If the desired delivery type is not found (eg. delivery by truck), the default delivery type by bike is returned.

Let’s test this:

5) Prototype (Clone)

This design pattern is a good choice when you need to create copies of an existing object but you don’t want to be dependent on object classes.

The main signature of this pattern is clone method that is often used in JavaScript libraries.

The problem this pattern solves is the tedious process of copying the object. First, you need to create a copy object.

Then you need to go through all the class fields in the original object and copy their values over to the copy object.

This process is ok until you realize that there are private fields in some objects which are not visible outside of the object.

All these problems are solved by the Prototype pattern.

A good example of the cloning process is zombies in video games or movies. If you get bitten by a zombie, you will become a zombie yourself.

Let’s implement this in the code.

In the code, we can see the IZombie interface with clone and bite methods.

Next, there is the Zombie class which implements the interface and creates a basic zombie, the green one with a strength of 50.

There is also a Zombies class, which contains a map of all zombies and also methods add and get.

Method add is for adding the zombie into a collection, and get will clone the specific zombie and return it.

We can test it like this:

That’s all for the first part. Hope you find it useful.

Stay tuned for the next part!

To all the senior developers yearning for remote software jobs with esteemed US and UK companies, your quest ends here. Index.dev opens doors to opportunities that resonate with your expertise. Index.dev engineers relish competitive salaries, exceeding market averages across countries. 

Register now and let the remote revolution redefine your career →