Factory Method Pattern in TypeScript— ✨ Creational Design Pattern #1
🙋🏻♀️ What is the Factory Method Pattern?
The Factory Method Pattern is a design approach that allows to create objects without specifying the exact class that will be created.
The idea behind the Factory Method Pattern is to define an interface or abstract class for creating objects, and then let the subclasses decide which class to instantiate.
The Factory Method Pattern is named after a manufacturing analogy because it is a pattern that is used to create objects in a way that is similar to how factories create products, emphasizing the creation of objects in a centralized and organized way,
In a factory, raw materials are processed and assembled into finished products by specialized workers and machinery. The finished products are then packaged and distributed to customers.
Similarly, in the Factory Method Pattern, an object is created by specialized code (the factory) that takes input parameters and assembles them into a finished object.
✨ Simple TypeScript Example
In this pattern, a factory method is defined in an interface or abstract class, which is responsible for creating objects of different types.
The concrete implementations of the factory method create specific types of objects based on the inputs provided.
Let’s start by looking at the abstract Advertisement
class here:
abstract class Advertisement {
abstract calculate(): number;
}
This abstract class defines the interface for the different types of advertisements we want to create using the Factory Method Pattern.
It has a single abstract method calculate
, which will be implemented by concrete advertisement classes that extend this abstract class. The method will return a number representing the cost of the advertisement.
Now let’s look at the two concrete advertisement classes:
class BasicAdvertisement extends Advertisement {
calculate(): number {
return 100;
}
}
class CostPerClickAdvertisement extends Advertisement {
calculate(): number {
return 90;
}
}
These classes are concrete implementations of the Advertisement
interface. They provide their own implementation of the calculate
method, which will be called by client code when the cost of the advertisements is calculated.
Next, let’s look at the AdvertisementFactory
class:
class AdvertisementFactory {
static create(type: 'basic' | 'cpc'): Advertisement {
switch (type) {
case "basic":
return new BasicAdvertisement();
case "cpc":
return new CostPerClickAdvertisement();
default:
throw new Error("Invalid advertisement type");
}
}
}
This class is the factory that creates advertisements. It has a static method create
that takes a type
, representing the type of advertisement to create.
The method uses a switch statement to determine which type of advertisement to create and returns an instance of the corresponding concrete advertisement class.
Finally, let’s look at the client code:
const basicAd = AdvertisementFactory.create("basic");
basicAdCost = basicAd.calculate(); //basicAdCost is 100
const cpcAd = AdvertisementFactory.create("cpc");
cpcAdCost = cpcAd.calculate(); // cpcAdCost is 90
This code shows how the factory is used to create advertisements.
It calls the create
method of the AdvertisementFactory
class with the appropriate advertisement type, and receives an instance of the corresponding concrete advertisement class. The client code can then call the calculate
method of the advertisement to calculate its cost, respectively.
🧑🏻💻 Use it or Avoid it
When to use it
The Factory Method Pattern is useful in situations where the exact type of object to be created is not known at compile time. It can help to decouple the client code from the object creation process, allowing more flexibility.
It is also useful when there are a large number of objects with similar creation processes, as the factory method can help to simplify the creation code.
class AdvertisementFactory {
static create(type: AdvertisementType): Advertisement {
switch (type) {
case "basic":
return new BasicAdvertisement();
case "cpc":
return new CostPerClickAdvertisement();
case "gov":
return new GovernmentSponsoredAdvertisement();
case "summer":
return new SummerSpecialAdvertisement();
// ... more cases ...
default:
throw new Error("Invalid advertisement type");
}
}
}
When to avoid it
However, using the Factory Method Pattern may not be desirable in some cases. You might imagine some cases where it is better not to produce certain product lines in our factory (if you are a factory manager).
If there are only a couple of types of objects that need to be created and their creation process is straightforward, or when the overhead of creating a factory class outweighs the benefits of using the pattern, it’s not the best time to use it.
It’s also not a good idea to use this pattern when the creation process is complex and requires customization for each object, making it difficult to create a generic factory method.
In short, although it’s not always necessary or desirable, especially if there are only a few simple objects to create or if the creation process is complex and requires customization, the Factory Pattern is useful for creating objects dynamically and decoupling client code from the creation process.