Factory pattern is used when we don’t want to instantiate objects directly, instead, we want to generate the instance(s) based on some criteria. The factory is responsible for generating the objects.
NOTES
In this article, we discuss the implementation of the Factory Pattern in TypeScript.
See the Factory in other languages in the “Other Code Implementations” section. Or, use the link below to check the details of the Factory Pattern-
Implementation
Use the following steps to implement Factory pattern in TypeScript –
- Create an interface, that will ensure a common interface for all the item concrete classes. Here we have created “Base” interface.
- Create concrete classes for items. Here we have “ExampleClass1” and “ExampleClass2” classes.
- Implement the interface (Base interface) for the concrete classes.
- Create a factory class. In the class, create a function that will accept some identifier, and based on the identifier it will generate and return an object.
Here is a simple example of Factory pattern implementation in TypeScript-
// --- Declare an interface ---
// Interface for the item classes that we want to generate object of
interface Base {
// Declare functions as per your requirement
operation1(): void;
operation2(): void;
}
// --- Interface definition end ---
// --- Define multiple item classes as per requirement ---
// Define item class
class ExampleClass1 implements Base {
operation1(): void {
console.log("ExampleClass1 operation1");
}
operation2(): void {
console.log("ExampleClass1 operation2");
}
}
// Define another item class
class ExampleClass2 implements Base {
operation1(): void {
console.log("ExampleClass2 operation1");
}
operation2(): void {
console.log("ExampleClass2 operation2");
}
}
// --- Item class definition end here ---
// --- Define factory class ---
class Factory {
// Make this method static if you prefer
public getExampleObj(type: string): (Base | null) {
// Generate object based on the parameter passed
if (type.toLocaleLowerCase() === "example_class_1") {
return new ExampleClass1();
}
if (type.toLocaleLowerCase() === "example_class_2") {
return new ExampleClass2();
}
return null;
}
}
// --- Factory class definition end ---
// --- Demo ---
// Create a factory object
const exampleFactory = new Factory();
// Use the factory object to generate the item classe object
const exampleObj1 = exampleFactory.getExampleObj("example_class_1");
exampleObj1?.operation1();
exampleObj1?.operation2();
// Use factory to generate another item object
const exampleObj2 = exampleFactory.getExampleObj("example_class_2");
exampleObj2?.operation1();
exampleObj2?.operation2();
// --- Demo end ---
Output of the above code will be as below
ExampleClass1 operation1
ExampleClass1 operation2
ExampleClass2 operation1
ExampleClass2 operation2
Examples
Let’s take a look at a few examples of Factory pattern implementation in TypeScript.
Example #1: Transport
Let’s take the example of a transport system. We are using the simple Factory implementation in this example.
Here we have 3 item classes – “Bus”, “Car”, and “Plane”. Instead of generating object of these classes directly, we will use the Factory pattern to generate the item class objects.
Let’s take a look at the implementation step-by-step.
Transport Interface
- Create a file named “transport.ts”.
- Define an interface named “Transport”.
- Declare functions required for the item classes. Here we have declared functions named “start”, “stop”, and “repair”. These functions are required for our item classes (“Bike”, “Car”, “Plane”) implementation.
// tranport.ts
interface Transport {
start(): void;
stop(): void;
repair(): void;
}
export default Transport;
Bike Class [Item Class]
- Create a file for the “Bike” class and name it “bike.ts”.
- Define a class named “Bike” and implement the “Transport” interface.
- Define the functions as part of “Transport” interface.
- Other fields and/or functions and be defined as per requirement.
// bike.ts
import Transport from "./transport";
class Bike implements Transport {
start(): void {
console.log("Bike started");
}
stop(): void {
console.log("Bike Stopped");
}
repair(): void {
console.log("Bike Repair");
}
}
export default Bike;
Car Class [Item Class]
- Create a file “car.ts”, and define the item class “Car” in the file.
- Implement interface “Transport” for the “Car” class.
// car.ts
import Transport from "./transport";
class Car implements Transport {
start(): void {
console.log("Car started");
}
stop(): void {
console.log("Car Stopped");
}
repair(): void {
console.log("Car Repair");
}
}
export default Car;
Plane Class [Item Class]
- Create a file “plane.ts”.
- Define the item class “Plane” in the file.
- Implement interface “Transport”.
// plane.ts
import Transport from "./transport";
class Plane implements Transport {
start(): void {
console.log("Plane started");
}
stop(): void {
console.log("Plane Stopped");
}
repair(): void {
console.log("Plane Repair");
}
}
export default Plane;
Transport Factory Class
- Create a file for the factory, named “transport-factory.ts”.
- Define the item class named “TransportFactory” in the file.
- Create a function named “getTransport”, which accepts an identifier (a string identifier in this case). Based on the identifier it will generate object of a class. If we pass “bike” it will generate a “Bike” object, if we pass “plane” it will generate a “Plane” class object, etc. If the identifier does not match any criteria, then it will return null.
You can make the getTransport function static, if you prefer.
// transport-factory.ts
import Transport from "./transport";
import Bike from "./bike";
import Car from "./car";
import Plane from "./plane";
class TransportFactory {
// Make this method static if needed
getTransport(type: string): (Transport | null) {
if (type.toLocaleLowerCase() === "bike") {
return new Bike();
}
if (type.toLocaleLowerCase() === "car") {
return new Car();
}
if (type.toLocaleLowerCase() === "plane") {
return new Plane();
}
return null;
}
}
export default TransportFactory;
Demo
- Create a file “demo.ts”.
- Create an object of “TransportFactory”.
- Call the “getTransport” function of the object to generate an object of item classes(Bus, Car, or Plane).
// demo.ts
import TransportFactory from "./transport-factory";
const transportFactory = new TransportFactory();
const transport1 = transportFactory.getTransport("bike");
transport1?.start();
const transport2 = transportFactory.getTransport("car");
transport2?.start();
transport2?.stop();
const transport3 = transportFactory.getTransport("plane");
transport3?.start();
Output
We will get the following output if the above demo code is executed.
Bike started
Car started
Car Stopped
Plane started
Example #2: Transport [Factory Method]
This example demonstrates the Factory method implementation for transport.
In this implementation, we have 2 types of transport- road transport and air transport. Multiple item classes belong to those types of transport.
Transport Interface
- Create a file named “transport.ts”.
- Declare interface named “Transport”.
- Declare some methods, according to the requirement. Here we have “start”, “stop”, “repair” methods.
// tranport.ts
interface Transport {
start(): void;
stop(): void;
repair(): void;
}
export default Transport;
Bus Class [Road Transport – Concrete Item Class]
Bus is part of road transport. Let’s define the “Bus” class-
- Create file named “bus.ts” for the “Bus” item class.
- Define the class “Bus”, and implement the “Transport” interface for the class.
// bus.ts
import Transport from "../transport/transport";
class Bus implements Transport {
start(): void {
console.log("Bus started");
}
stop(): void {
console.log("Bus Stopped");
}
repair(): void {
console.log("Bus Repair");
}
}
export default Bus;
Bike Class [Road Transport – Concrete Item Class]
Bike is part of road transport. Let’s define the “Bike” class-
- Create file named “bike.ts” for the “Bike” item class.
- Define the class “Bike”, and implement “Transport” interface for the class.
// bike.ts
import Transport from "../transport/transport";
class Bike implements Transport {
start(): void {
console.log("Bike started");
}
stop(): void {
console.log("Bike Stopped");
}
repair(): void {
console.log("Bike Repair");
}
}
export default Bike;
Car Class [Road Transport – Concrete Item Class]
Car is part of road transport. Let’s define the “Car” class-
- Create a file named “car.ts”.
- Define class “Car”, and implement “Transport” interface for the class.
// car.ts
import Transport from "../transport/transport";
class Car implements Transport {
start(): void {
console.log("Car started");
}
stop(): void {
console.log("Car Stopped");
}
repair(): void {
console.log("Car Repair");
}
}
export default Car;
Plane Class [Air Transport – Concrete Item Class]
Plane is part of air transport. Let’s define the “Plane” class-
- Create a file named “plane.ts”.
- Define class “Plane”, and implement “Transport” interface for the class.
// plane.ts
import Transport from "../transport/transport";
class Plane implements Transport {
start(): void {
console.log("Plane started");
}
stop(): void {
console.log("Plane Stopped");
}
repair(): void {
console.log("Plane Repair");
}
}
export default Plane;
Helicopter Class [Air Transport – Concrete Item Class]
Helicopter is part of air transport. Let’s define the “Helicopter” class-
- Create a file named “helicopter.ts”.
- Define class “Helicopter”, and implement “Transport” interface.
// helicopter.ts
import Transport from "../transport/transport";
class Helicopter implements Transport {
start(): void {
console.log("Helicopter started");
}
stop(): void {
console.log("Helicopter Stopped");
}
repair(): void {
console.log("Helicopter Repair");
}
}
export default Helicopter;
Now we have to define the factory classes. We have a total of 3 classes for factory here – the factory abstract class that will be extended by other factory classes, and 2 factory classes.
Transport Factory [Abstract Class]
- Create a file named “transport-factory.ts”.
- Define the class “TransportFactory”, and make it abstract.
- Define functions like – operateTransport, repairTransport for performing operations.
- Declare a abstract method “getTransport”, so that child classes can define the method as per requirement.
// transport-factory.ts
import Transport from "./transport";
abstract class TransportFactory {
operateTransport(name: string): void {
const transport = this.getTransport(name);
transport?.start();
transport?.stop();
}
repairTransport(name: string): void {
const transport = this.getTransport(name);
transport?.repair();
}
abstract getTransport(name: string): (Transport | null);
}
export default TransportFactory;
Road Transport Factory
- Create a file “road-transport-factory.ts”.
- Create a class named “RoadTransportFactory” and extend the “TransportFactory” abstract class.
- Define the function “getTransport” as the part of “TransportFactory” abstract class implementation. Check the passed param “name” value and based on that decide which object needs to be generated-“Car” or “Bike” or “Bus”.
// road-transport-factory.ts
import Car from "../transport/car";
import TransportFactory from "./transport-factory";
import Bus from "./bus";
import Bike from "./bike";
import Transport from "./transport";
class RoadTransportFactory extends TransportFactory {
getTransport(name: string): (Transport | null) {
if (name.toLowerCase() === "car") {
return new Car();
}
if (name.toLowerCase() === "bike") {
return new Bike();
}
if (name.toLowerCase() === "bus") {
return new Bus();
}
return null;
}
}
export default RoadTransportFactory;
Air Transport Factory
- Create a file “air-transport-factory.ts”.
- Create a class named “AirTransportFactory” and extend the “TransportFactory” abstract class. It will be similar to the “RoadTransportFactory” implementation.
- Define the function “getTransport” as the part of “TransportFactory” abstract class implementation.
// air-transport-factory.ts
import Plane from "../transport/plane";
import TransportFactory from "./transport-factory";
import Helicopter from "./helicopter";
import Transport from "./transport";
class AirTransportFactory extends TransportFactory {
getTransport(name: string): (Transport | null) {
if (name.toLowerCase() === "plane") {
return new Plane();
}
if (name.toLowerCase() === "helicopter") {
return new Helicopter();
}
return null;
}
}
export default AirTransportFactory;
Demo
- Create a file named “demo.ts”.
- Create instances of “RoadTransportFactory” and/or “AirTransportFactory”.
- Call functions defined in the “TransportFactory” to use the functionality. Here we have called the “operateTransport” function.
// demo.ts
import TransportFactory from "./transport-factory";
import AirTransportFactory from "./air-transport-factory";
import RoadTransportFactory from "./road-transport-factory";
const roadTransportFactory: TransportFactory = new RoadTransportFactory();
const airTransportFactory: TransportFactory = new AirTransportFactory();
roadTransportFactory?.operateTransport("bus");
airTransportFactory.operateTransport("helicopter");
roadTransportFactory.repairTransport("bike");
Output
The above demo code will generate the result below.
Bus started
Bus Stopped
Helicopter started
Helicopter Stopped
Bike Repair
Source Code
Use the following link to get the source code:
Other Code Implementations
Use the following links to check Factory pattern implementation in other programming languages.