DEV Community

Cover image for Factory Design Pattern
Nabin Ale
Nabin Ale

Posted on • Updated on

Factory Design Pattern

The Factory Design Pattern is a way to create objects without specifying the exact class of the object that will be created. It provides a method to create objects that can be overridden by subclasses to change the type of objects that will be created.

Understanding through Example

Let's use the Factory Design Pattern with an example involving a waiter, a cook, and the client in a restaurant setting. Here’s how the pattern works in this context:

a. Waiter: Acts as the Factory.
b. Cook: Represents the different types of objects that can be created.
c. Client: Requests a dish through the waiter.

Scenario

A client goes to a restaurant and asks the waiter for a specific dish (e.g., Momo or Pizza). The waiter takes the order and instructs the cook to prepare the dish. The client doesn't know the details of how the dish is prepared; they just get the dish.

Step-by-Step Example in TypeScript

Step 1: Define the Dish Interface

interface Dish {
    prepare(): void;
}
Enter fullscreen mode Exit fullscreen mode

This interface defines a method prepare() that every dish must implement.

Step 2: Create Concrete Classes for Dishes

class Momo implements Dish {
    prepare(): void {
        console.log("Preparing Momo");
    }
}

class Pizza implements Dish {
    prepare(): void {
        console.log("Preparing Pizza");
    }
}
Enter fullscreen mode Exit fullscreen mode

These classes implement the Dish interface and provide their own version of the prepare method.

Step 3: Create the Waiter (Factory)

class Waiter {
    static orderDish(dishType: string): Dish | null {
        switch (dishType) {
            case 'Momo':
                return new Momo();
            case 'Pizza':
                return new Pizza();
            default:
                return null;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

The Waiter class has a static method orderDish that takes a string parameter dishType and returns an instance of the corresponding dish.

Step 4: Client Requests a Dish

const dish1: Dish | null = Waiter.orderDish('Momo');
if (dish1) {
    dish1.prepare();  // Output: Preparing Momo
}

const dish2: Dish | null = Waiter.orderDish('Pizza');
if (dish2) {
    dish2.prepare();  // Output: Preparing Pizza
}
Enter fullscreen mode Exit fullscreen mode

The client uses the Waiter to order dishes by specifying the dish type. The Waiter (factory) handles the creation of the dish, and the client receives a dish ready to be prepared.

Explanation

- Dish Interface: This defines a common interface that all dishes must follow.
- Concrete Classes (Momo, Pizza): Implement the Dish interface and provide specific implementations for the prepare method.
- Waiter (Factory): Contains the orderDish method, which decides which dish to create based on the given type.
- Client: Requests dishes from the Waiter without needing to know the details of how the dishes are created.

Full given Example in TypeScript look like this

// Dish Interface
interface Dish {
    prepare(): void;
}

// Concrete Momo Class
class Momo implements Dish {
    prepare(): void {
        console.log("Preparing Momo");
    }
}

// Concrete Pizza Class
class Pizza implements Dish {
    prepare(): void {
        console.log("Preparing Pizza");
    }
}

// Waiter (Factory) Class
class Waiter {
    static orderDish(dishType: string): Dish | null {
        switch (dishType) {
            case 'Momo':
                return new Momo();
            case 'Pizza':
                return new Pizza();
            default:
                return null;
        }
    }
}

// Client Code
const dish1: Dish | null = Waiter.orderDish('Momo');
if (dish1) {
    dish1.prepare();  // Output: Preparing Momo
}

const dish2: Dish | null = Waiter.orderDish('Pizza');
if (dish2) {
    dish2.prepare();  // Output: Preparing Pizza
}

Enter fullscreen mode Exit fullscreen mode

To execute the code within a web browser click here

Why use the Factory Design Pattern?

1. Decoupling: It decouples the client code from the object creation process. It separates the part of the code that asks for an object from the part that makes the object. This way, the code that needs the object doesn't have to know exactly how the object is made
2. Flexibility: It allows the code to be more flexible and easier to extend. New classes can be added without changing the existing client code.
3. Single Responsibility: The Factory Design Pattern follows the Single Responsibility Principle by making sure that each class has only one job

Summary

In this example, the client interacts only with the Waiter (factory), asking for specific dishes. The Waiter then decides which type of dish to create and returns it to the client. This pattern decouples the client from the creation logic, making the system more modular and easier to maintain.

Top comments (0)