TeachingBee

Factory Design Pattern : Low Level Design

factory design pattern

The Factory Design Pattern is one of the most commonly used design patterns in object-oriented software development. It falls under the category of creational patterns, as it deals with object creation mechanisms.

The pattern aims to create objects without specifying the exact class of object that will be created. This is achieved by defining an interface for creating an object, but letting subclasses decide which class to instantiate. The Factory Method lets a class defer instantiation to subclasses.

What is factory design pattern with real time example?

In essence, the Factory Design Pattern suggests defining an interface or abstract class for creating an object but allowing the subclasses to choose the class to be instantiated. This pattern is particularly useful when there is a need to manage and maintain a large number of derived classes derived from a common interface or base class. Let’s understand via real time example.

For Example

factory design pattern

Imagine you own a toy factory that makes different types of toys: cars, planes, and dolls. You have a general plan (interface or abstract class) for making a toy, which includes steps like designing, assembling, and painting. However, the specifics of creating each type of toy vary significantly.

In this scenario, the Factory Design Pattern is like having a general manager (the interface or abstract class) who knows the steps to make a toy but not the specifics of each type. When it’s time to make a new toy, the general manager decides which specific toy factory (subclass) to use based on the toy type requested.

  • If a customer wants a car, the general manager directs the request to the car factory (subclass) which knows how to assemble cars specifically.
  • If the order is for a plane, the plane factory (another subclass) takes over, using its expertise to build planes.
  • For dolls, the request goes to the doll factory (yet another subclass) that specializes in creating dolls.

This approach is helpful when managing many types of toys (or derived classes) that share a common creation process (interface or base class) but have their own specific manufacturing details.

Why Do We Need the Factory Design Pattern?

The need for the Factory Design Pattern arises in scenarios where a system should be independent of how its objects are created, composed, and represented. It provides a way to encapsulate object creation so that the type of objects used in a system can be specified at runtime.

Let’s understand this via code example.

Imagine you are developing an application that requires different types of notification services: email, SMS, and push notifications. Initially, you might start with a straightforward approach like this:

// Straightforward Approach

class NotificationService {
    void sendNotification(String type, String message) {
        if (type.equals("Email")) {
            // Code to send email
            System.out.println("Sending Email: " + message);
        } else if (type.equals("SMS")) {
            // Code to send SMS
            System.out.println("Sending SMS: " + message);
        } else if (type.equals("Push")) {
            // Code to send Push Notification
            System.out.println("Sending Push Notification: " + message);
        }
    }
}

There are few issues with this code.

  1. Violation of Single Responsibility Principle (SRP): The NotificationService class is responsible for determining the type of notification to send and sending the notification itself. This violates the SRP, as a class should have only one reason to change. If there are changes in the way notifications are sent or in the types of notifications, this class will need modification.
  2. Lack of Extensibility: Adding a new type of notification (e.g., “Fax”, “Pager”, etc.) would require modifying the sendNotification method, which violates the Open/Closed Principle. Every time you want to add a new notification type, you’d have to modify this class.
  3. Code Duplication: The code for sending each type of notification is embedded within the sendNotification method. This violates the DRY (Don’t Repeat Yourself) principle, as the same code for sending notifications is repeated for each type.

Solving the Issue with the Factory Design Pattern

The Factory Design Pattern can address this problem by encapsulating the creation logic for different notification types, thus decoupling the client code from the concrete classes.

Let’s look how to solve the above problem with factory design pattern.

How do you implement a factory design pattern?

Implementing the Factory Design Pattern involves creating a factory class that is responsible for creating and returning instances of classes based on the provided criteria.

factory design pattern

Components of the Factory Design Pattern

  1. Product Interface: This defines the contract for the products created by the factory.
  2. Concrete Products: These are the specific implementations of the product interface.
  3. Creator (Factory) Interface: An interface that declares the factory method, which returns an object of the product interface.
  4. Concrete Creator: Implements the factory method to create and return concrete products.

Step-by-Step Implementation

factory design pattern in notification system

Let’s see how to implement these components to solve above problem.

  1. Define Product Interface: We will begin by defining interface for different type of notification service which each notification service will implement.
interface Notification {
    void send(String message);
}
  1. Implement Concrete Products: Implement each notification service in our case Email, SMS etc which will implement Notification interface.
class EmailNotification implements Notification {
    public void send(String message) {
        System.out.println("Sending Email: " + message);
    }
}

class SMSNotification implements Notification {
    public void send(String message) {
        System.out.println("Sending SMS: " + message);
    }
}

class PushNotification implements Notification {
    public void send(String message) {
        System.out.println("Sending Push Notification: " + message);
    }
}
  1. Define Factory Interface (Optional in some cases):

In simple implementations, you might directly implement a factory class without needing an interface.

  1. Implement Concrete Factory: Notification Factory which will decide which object to return based on type.
class NotificationFactory {
    public static Notification createNotification(String type) {
        switch (type) {
            case "Email":
                return new EmailNotification();
            case "SMS":
                return new SMSNotification();
            case "Push":
                return new PushNotification();
            default:
                throw new IllegalArgumentException("Unknown notification type");
        }
    }
}
  1. Usage: Client code will call notification factory and get object of type Notification. Now the object creation is isolated from client code.
public class Main {
    public static void main(String[] args) {
        Notification notification = NotificationFactory.createNotification("Email");
        notification.send("Hello, World!");
    }
}

By using the Factory Design Pattern:

  1. Single Responsibility Principle: Each class (EmailNotification, SMSNotification, PushNotification) is responsible for sending a specific type of notification. The creation of these objects is delegated to the NotificationFactory, separating the concerns of creation and sending.
  2. Extensibility: Adding a new type of notification is as simple as creating a new implementation of the Notification interface and updating the createNotification method in the NotificationFactory class. No modifications to existing client code are necessary.
  3. Code Duplication: Each notification type is encapsulated within its own class, eliminating code duplication.

Complete Java Code

// Notification interface
interface Notification {
    void send(String message);
}

// Concrete implementation for Email notification
class EmailNotification implements Notification {
    public void send(String message) {
        System.out.println("Sending Email: " + message);
    }
}

// Concrete implementation for SMS notification
class SMSNotification implements Notification {
    public void send(String message) {
        System.out.println("Sending SMS: " + message);
    }
}

// Concrete implementation for Push notification
class PushNotification implements Notification {
    public void send(String message) {
        System.out.println("Sending Push Notification: " + message);
    }
}

// Factory class for creating instances of different notification types
class NotificationFactory {
    public static Notification createNotification(String type) {
        switch (type) {
            case "Email":
                return new EmailNotification();
            case "SMS":
                return new SMSNotification();
            case "Push":
                return new PushNotification();
            default:
                throw new IllegalArgumentException("Unknown notification type");
        }
    }
}

// Main class
public class Main {
    public static void main(String[] args) {
        // Create an Email notification
        Notification notification = NotificationFactory.createNotification("Email");
        notification.send("Hello, World!");
    }
}

Common Usage in Applications

The Factory Design Pattern is widely used in software development. Common applications include:

  • UI libraries and frameworks where different styles of elements are needed depending on the environment.
  • In API libraries where the implementation might change based on the system configuration or usage context.
  • Managing resource-intensive objects whose instantiation process should be controlled or optimized.
  • LogFactory where we need to manage the creation of different types of loggers (such as file loggers, database loggers, or console loggers) based on the runtime environment or a configuration setting.
  • DataSourceFactory where we need to create connections to different types of data sources (such as SQL databases, NoSQL databases, or XML files) based on application requirements.

Identifying Where to Apply Factory Design Pattern

So, now important question arises i.e. how to Identify scenarios for the Factory Design Pattern ? . USe Factory Pattern in situations where:

  • Complexity in Object Creation: When object creation involves more than just instantiation and includes setting up state, dependencies, or configuration.
  • Need for Flexibility: When your application needs to introduce new types of objects dynamically without changing existing code.
  • Decoupling: When you want to reduce dependencies between your application’s code and the concrete classes you use.
  • Consistency: When you have a set of related or dependent objects that should always be used together.
  • Control Over Instantiation: When you need to control the instantiation process of an object, such as using pooling or managing limited resources.

By recognizing these scenarios in your application development, you can effectively apply the Factory Design Pattern to improve flexibility, maintainability, and scalability of your software design.

Benefits and Drawbacks Of Factory Design Pattern

Let’s see pros and cons of factory design pattern.

Benefits Of Factory Design Pattern

  • Flexibility: Allows for objects to be created at runtime based on the required criteria.
  • Decoupling: The client code is decoupled from the concrete classes, adhering to the principle of programming to interfaces, not implementations.
  • Extensibility: Adding new types of products without disturbing existing client code.

Drawbacks Of Factory Design Pattern

  • Complexity: Can introduce additional complexity into the code, especially if overused for scenarios that do not require such flexibility.
  • Code Maintenance: Might increase the number of classes and interfaces, making the system harder to understand for new developers.

Key TakeAways

  • Strings in C are arrays of characters terminated by a null character ‘\0’. They are stored in char arrays and can be manipulated using various string functions.
  • Strings can be declared by specifying a char array of fixed size, without size (allowing automatic size calculation), or assigned character by character. Initialization can be done using string literals or individual character assignments.
  • The difference between character arrays and string literals is that character arrays are modifiable, occupy memory dynamically, and need explicit initialization, while string literals are stored in read-only memory, have fixed sizes, and initialize automatically.
  • Passing strings to functions can be done by passing the character array. The array contains the characters of the string which gets passed to the function through this mechanism.

Similar Posts

How Factory Design Pattern Is Used in Spring Boot?

How to Apply Factory Design Pattern for Payment System

Can We Use Factory Design Pattern in Dependency Injection?

What Is the Difference Between Factory and Abstract Factory Design Patterns?

When to Use Factory Design Pattern in Java?

FAQ

What is the main goal of the factory design pattern?

What is factory design pattern with real time example?

What are the types of factory pattern?

What is the benefit of factory pattern?

90% of Tech Recruiters Judge This In Seconds! 👩‍💻🔍

Don’t let your resume be the weak link. Discover how to make a strong first impression with our free technical resume review!

Related Articles

Types of Memory and Storage in system design thumbnail

Types of Computer Memory and Storage

In this article we will look into different types of computer memory, distinguishing between primary memory and secondary memory. We will also see their characteristics, major kinds, usage, and key

latency in system design thumbnail

What is Latency In System Design?

In this article we will look into Latency In System Design, we will see how is latency introduced into the systems and what are various ways to reduce the latency

Why Aren’t You Getting Interview Calls? 📞❌

It might just be your resume. Let us pinpoint the problem for free and supercharge your job search. 

Newsletter

Don’t miss out! Subscribe now

Log In