23 Jan 2024

Factory Pattern

Factory pattern

The Factory Method Pattern is a creational design pattern that provides an interface for creating objects in a superclass but allows subclasses to alter the type of objects that will be created. This pattern defines an interface for creating an object, but it is the responsibility of the derived classes to implement and instantiate the object. The Factory Method Pattern allows a class to delegate the responsibility of instantiating its objects to its subclasses. This promotes flexibility by allowing a class to specify the type of objects it creates, but deferring the instantiation details to its subclasses. It is particularly useful in scenarios where a class cannot anticipate the class of objects it must create.

Key components of the Factory Method Pattern include:

  • Product interface

  • Concreate product

  • Creator interface

  • Concrete creators

Product Interface

This is the interface or abstract class that declares the creation method. It defines the common interface for all concrete products.

  • Definition: The Product Interface defines the common interface for all concrete products created by the factory method.

  • Purpose: It establishes the structure that concrete products must adhere to, ensuring they provide a consistent set of methods or attributes.

Concrete Products

These are the specific implementations of the product interface, created by the factory method.

  • Definition: Concrete Products are the actual objects created by the factory method. They implement the Product Interface.

  • Purpose: Each Concrete Product represents a specific type of object created by the factory. They provide concrete implementations for the methods defined in the Product Interface.

Creator Interface

The interface or abstract class that declares the factory method for creating products. It may also include other methods that operate on products.

  • Definition: Concrete Products are the actual objects created by the factory method. They implement the Product Interface.

  • Purpose: Each Concrete Product represents a specific type of object created by the factory. They provide concrete implementations for the methods defined in the Product Interface.

Concrete Creators

These are the subclasses that implement the factory method to produce concrete products.

  • Definition: Concrete Creators are subclasses that implement the Creator Interface. They provide specific implementations for the factory method, creating instances of Concrete Products.

  • Purpose: Each Concrete Creator is responsible for creating a particular type of Concrete Product, adhering to the common Creator Interface.

Example

consider an organization were there are many employees. The employees are broadly classified into developers and managers. the organization is in need for a system for managing employees in a software development company. The company employs two types of professionals: Developers and Managers. Each professional has specific attributes, and the system should support the creation of these employees

// Product Interface
interface Employee {
    name: string;
    salary: number;
    getDetails(): string;
}

// Concrete Products
class Developer implements Employee {
        constructor(
        public name: string,
        public salary: number,
        public programmingLanguage: string,
        public experienceYears: number
    ) {}

    getDetails(): string {
        return `Developer: ${this.name}, Salary: ${this.salary}, Languages: ${this.programmingLanguage}, Experience: ${this.experienceYears} years`;
    }
}

class Manager implements Employee {
        constructor(
        public name: string,
        public salary: number,
        public department: string,
        public teamSize: number
    ) {}

    getDetails(): string {
        return `Manager: ${this.name}, Salary: ${this.salary}, Department: ${this.department}, Team Size: ${this.teamSize}`;
    }
}

// Creator Interface
interface EmployeeFactory {
    createEmployee(name: string, salary: number): Employee;
}

// Concrete Creators
class DeveloperFactory implements EmployeeFactory {
    createEmployee(name: string, salary: number): Employee {
        
        return new Developer(name, salary, "JavaScript ", 3);
    }
}

class ManagerFactory implements EmployeeFactory {
    createEmployee(name: string, salary: number): Employee {
        
        return new Manager(name, salary, "Engineering", 10);
    }
}

// Client code
function printEmployeeDetails(factory: EmployeeFactory, name: string, salary: number): void {
    const employee = factory.createEmployee(name, salary);
    console.log(employee.getDetails());
}

//  usage
const developerFactory: EmployeeFactory = new DeveloperFactory();
const managerFactory: EmployeeFactory = new ManagerFactory();

printEmployeeDetails(developerFactory, "Dev", 60000);
printEmployeeDetails(managerFactory, "Mangal", 80000);

Explanation:

  • Product Interface -(Employee):

    • Represents the common interface for all concrete products. It has attributes (name, salary) and a method (getDetails).

  • Concrete Products -(Developer and Manager):

    • Implement the Employee interface. They provide specific implementations for the getDetails method.

  • Creator Interface (EmployeeFactory):

    • Declares the factory method createEmployee responsible for creating products. It is an abstraction for creating objects without specifying their exact type.

  • Concrete Creators -(DeveloperFactory and ManagerFactory):

    • Implement the EmployeeFactory interface. They provide specific implementations of the factory method to create instances of concrete products (Developer and Manager)

Pros and cons

Avoiding tight coupling between the creator and the specific products promotes the Single Responsibility Principle. By consolidating the product creation code in a single location within the program, maintenance becomes more straightforward.

Additionally, adhering to the Open/Closed Principle allows for the introduction of new product types without disrupting existing client code. This principle enables the program to accommodate new products seamlessly while preserving compatibility with the existing codebase.

The complexity of the code may increase as you're required to introduce numerous new subclasses to implement the pattern. Ideally, the most favorable situation arises when incorporating the pattern into an existing hierarchy of creator classes.

Applicability

  • Utilize the Factory Method when the precise types and dependencies of objects for your code are unknown in advance. This pattern separates the construction code of a product from the code that utilizes the product, facilitating independent extension of the product construction code.

  • For instance, when adding a new product type to the application, creating a new creator subclass and overriding the factory method in it is all that's required.

  • Also, employ the Factory Method when providing users of your library or framework with a means to extend its internal components. While inheritance is a straightforward way to enhance default behavior, determining that your subclass should be used instead of a standard component poses a challenge. The Factory Method mitigates this by consolidating the code that constructs components into a single method, allowing users to override it and extend the component as needed.

  • Consider a scenario where an open-source UI framework offers square buttons, but your app requires round buttons. By creating a subclass (UIWithRoundButtons) from a base framework class and overriding its createButton method to return RoundButton objects, you seamlessly integrate the new button subclass into your app.

  • Additionally, employ the Factory Method when aiming to conserve system resources by reusing existing objects instead of rebuilding them every time. This is particularly relevant for large, resource-intensive objects like database connections, file systems, and network resources.

  • Creating a storage mechanism to track created objects, searching for a free object when requested, and returning it to the client code—all while handling scenarios where no free objects are available and new ones need creation—can be complex. Placing this code in a single location, such as a factory method, prevents code duplication and streamlines resource management.

← Back to Library