Abstract Factory | Deep Dive In Design Patterns

Narendra Singh Rathore
4 min readAug 15, 2024

--

The Abstract Factory pattern is a creational design pattern that provides an interface for creating families of related or dependent objects without specifying their concrete classes. It allows a client to create objects of a particular family without knowing the exact classes that will be instantiated.

Advantages

  1. Encapsulation: It hides the concrete classes and the instantiation process, making it easier to introduce new families of products without altering the client code.
  2. Consistency: Ensures that objects from the same family are used together, as the factory creates related objects.
  3. Scalability: Adding new product families or variants can be done by extending the existing abstract factory without modifying existing code.

Disadvantages

  1. Complexity: It can introduce additional layers of abstraction, which may make the codebase more complex and harder to understand.
  2. Overhead: If the system does not need to create products from multiple families, the pattern might introduce unnecessary abstraction and overhead.

Code Sample in TypeScript

Let’s create a simple example with an Abstract Factory pattern. Suppose we have a system that produces two types of UI components: Buttons and Checkboxes. We want to create different families of UI components for different operating systems: Windows and MacOS.

  1. Define Abstract Products
interface Button {
render(): void;
}

interface Checkbox {
render(): void;
}

2. Concrete Products for Windows

class WindowsButton implements Button {
render() {
console.log("Rendering a Windows button");
}
}

class WindowsCheckbox implements Checkbox {
render() {
console.log("Rendering a Windows checkbox");
}
}

3. Concrete Products for MacOS

class MacOSButton implements Button {
render() {
console.log("Rendering a MacOS button");
}
}

class MacOSCheckbox implements Checkbox {
render() {
console.log("Rendering a MacOS checkbox");
}
}

4. Define Abstract Factory

interface GUIFactory {
createButton(): Button;
createCheckbox(): Checkbox;
}

5. Concrete Factories

class WindowsFactory implements GUIFactory {
createButton(): Button {
return new WindowsButton();
}

createCheckbox(): Checkbox {
return new WindowsCheckbox();
}
}

class MacOSFactory implements GUIFactory {
createButton(): Button {
return new MacOSButton();
}

createCheckbox(): Checkbox {
return new MacOSCheckbox();
}
}

6. Client Code

class Application {
private button: Button;
private checkbox: Checkbox;

constructor(factory: GUIFactory) {
this.button = factory.createButton();
this.checkbox = factory.createCheckbox();
}

renderUI() {
this.button.render();
this.checkbox.render();
}
}

// Usage
const windowsFactory = new WindowsFactory();
const macOSFactory = new MacOSFactory();

const appForWindows = new Application(windowsFactory);
appForWindows.renderUI();

const appForMacOS = new Application(macOSFactory);
appForMacOS.renderUI();

How It Helps Improve Code

  • Flexibility: You can easily swap out WindowsFactory for MacOSFactory without modifying the Application class. This allows for easier maintenance and scalability.
  • Consistency: By using the factory, you ensure that Button and Checkbox objects are compatible and work well together, as they are created by the same factory.
  • Separation of Concerns: The Application class focuses on its core functionality and delegates object creation to the factory, adhering to the Single Responsibility Principle.

How it is different from factory pattern

The Abstract Factory pattern and the Factory Method pattern are both creational design patterns, but they serve different purposes and are used in different scenarios. Here’s a breakdown of their differences:

Factory Method Pattern

Purpose:

  • Provides a method for creating objects without specifying the exact class of the object that will be created. It allows a class to delegate the instantiation of objects to its subclasses.

Key Characteristics:

  • Defines an interface for creating an object but allows subclasses to alter the type of objects that will be created.
  • Typically, there is a single factory method for creating one type of product.

Usage:

  • Used when a class can’t anticipate the class of objects it must create.
  • Useful when subclasses are responsible for instantiating specific types of objects.

Example:

// Product interface
interface Product {
operation(): string;
}

// Concrete Products
class ConcreteProductA implements Product {
operation() {
return "Operation A";
}
}

class ConcreteProductB implements Product {
operation() {
return "Operation B";
}
}

// Creator
abstract class Creator {
abstract factoryMethod(): Product;

someOperation(): string {
const product = this.factoryMethod();
return `Creator: The same creator's code just worked with ${product.operation()}`;
}
}

// Concrete Creators
class ConcreteCreatorA extends Creator {
factoryMethod(): Product {
return new ConcreteProductA();
}
}

class ConcreteCreatorB extends Creator {
factoryMethod(): Product {
return new ConcreteProductB();
}
}

// Usage
const creatorA = new ConcreteCreatorA();
console.log(creatorA.someOperation()); // "Creator: The same creator's code just worked with Operation A"

const creatorB = new ConcreteCreatorB();
console.log(creatorB.someOperation()); // "Creator: The same creator's code just worked with Operation B"

Abstract Factory Pattern

Purpose:

  • Provides an interface for creating families of related or dependent objects without specifying their concrete classes. It allows a client to create objects from a particular family of products.

Key Characteristics:

  • Defines an abstract factory interface with multiple factory methods to create different types of products.
  • Often used when there are multiple families of products and you want to ensure that products from one family are used together.

Usage:

  • Used when a system needs to work with multiple families of related objects and you want to ensure that objects from one family are used together.
  • Helps manage dependencies between related objects and enforces consistency.

Thanks for reading

--

--

No responses yet