Introduction to the Abstract Factory Pattern

Welcome back! You’ve already explored the power of the Factory Method Pattern and how it promotes flexibility in your code design. Today, we are moving a step further by diving into the Abstract Factory Pattern. This pattern will help you create families of related objects without specifying their concrete classes.

Understanding the Abstract Factory Pattern

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. This pattern is particularly useful when you need to ensure that a set of related objects is created together, maintaining consistency across your application.

To understand this better, let's consider a scenario in which you are developing a graphical user interface (GUI) toolkit. Your toolkit should support multiple operating systems, such as Windows and Mac. Each OS has its own set of UI components, like buttons and checkboxes. Using the Abstract Factory Pattern, you can define interfaces for these components and create their concrete implementations for each OS.

Factory Method vs. Abstract Factory: What’s the Difference?

You might be wondering how the Abstract Factory Pattern differs from the Factory Method Pattern you learned earlier. Here’s a quick comparison to clarify:

  • Factory Method Pattern: Defines an interface for creating a single product, but lets subclasses decide which concrete class to instantiate. It focuses on creating one type of object.

    • Example: A Dialog class that creates a Button using a factory method. Subclasses like WindowsDialog or MacDialog override the method to return a specific Button implementation.
  • Abstract Factory Pattern: Provides an interface for creating families of related or dependent products (not just one), without specifying their concrete classes. It ensures that products created together are compatible.

    • Example: A GUIFactory that can create both Button and Checkbox objects, ensuring that both are from the same family (e.g., both Windows-style or both Mac-style).

In summary:

  • Use Factory Method when you need to create one product and let subclasses decide which one.
  • Use Abstract Factory when you need to create families of related products and ensure their compatibility.
Why Use Interfaces (and Not Abstract Classes) Here?

TypeScript provides both interfaces and abstract classes to define contracts for your code. In this lesson, we use interfaces for product and factory definitions because:

  • Interfaces are ideal for describing the shape of objects and are purely about defining contracts—what methods and properties a class must implement—without any implementation details.
  • Abstract classes can provide both a contract (abstract methods) and some shared implementation, but they cannot be implemented by plain objects and are less flexible for multiple inheritance.

For design patterns like Abstract Factory, interfaces are preferred when you only need to specify what methods must exist, and you don’t need to share any code between implementations. This keeps your code flexible and focused on the contract, which is especially useful when defining families of related products and factories.

If you need to provide some shared logic or default behavior, you could use an abstract class instead. However, in most cases for creational patterns in TypeScript, interfaces are sufficient and more idiomatic.

Defining Abstract Product Interfaces

TypeScript provides built-in support for interfaces and abstract classes, allowing you to define clear contracts for your components. Here’s how you can define abstract product interfaces for UI components:

In this setup, the Button and Checkbox interfaces declare the paint method, which must be implemented by all concrete classes.

Creating Concrete Product Implementations

Now, let's create concrete implementations of these products for Windows and Mac. In TypeScript, you use the implements keyword to ensure that classes adhere to the interface contracts:

Each concrete class implements the appropriate interface and provides its own version of the paint method.

Defining the Abstract Factory Interface

TypeScript allows you to define abstract factories using interfaces or abstract classes. Here, we use an interface to specify the factory contract:

The GUIFactory interface declares methods for creating buttons and checkboxes. Concrete factories will implement these methods to produce specific products.

Creating Concrete Factory Implementations

Concrete factories implement the GUIFactory interface and instantiate the corresponding concrete products. TypeScript’s type system ensures that the correct types are returned:

WinFactory creates Windows-specific products, while MacFactory creates Mac-specific components. This results in the consistent creation of related objects without referring to their concrete classes.

Implementing the Client Code

The client code interacts with the abstract factory to create and use the products. TypeScript’s type annotations help ensure type safety throughout the process:

The Application class demonstrates client operations with abstract interfaces, promoting flexibility and scalability.

Code Overview

Here is the complete code in TypeScript:

This example illustrates the Abstract Factory Pattern in a TypeScript environment, allowing for the dynamic creation and management of families of related objects with strong type safety and clear contracts.

Summary

The Abstract Factory Pattern is a powerful creational design pattern that enables you to create families of related objects without specifying their concrete classes. In TypeScript, interfaces are typically used to define the contracts for products and factories, keeping your code flexible and focused on what needs to be implemented, rather than how. This approach ensures that your application can easily support multiple product families (such as Windows and Mac UI components) while maintaining consistency and type safety. By separating the creation logic from the client code, the Abstract Factory Pattern promotes scalability, maintainability, and clear code organization.

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal