1.3 Abstract Factory - Creational Pattern : A Practical Approach

The Abstract Factory pattern is a prominent technique in the world of software design patterns, as it allows one to create families of linked or dependent items without having to specify their particular classes. This creational pattern offers a uniform interface for creating different kinds of objects while preserving flexibility and extensibility. It encompasses the creation of numerous related items. We'll examine the fundamentals of the Abstract Factory pattern, look at how it's implemented in Java, and use an actual example to demonstrate how to use it in this blog article.

Understanding the Abstract Factory Pattern:

The difficulty of constructing families of linked objects without defining their concrete classes is addressed by the Abstract Factory pattern. The idea of "abstract factories," which specify interfaces for making abstract products, is introduced. These interfaces are used by concrete companies to produce particular kinds of related goods. Usually, this pattern includes the following essential elements:

  1. Abstract Factory: An interface or abstract class called a "Abstract Factory" declares a number of methods for making abstract goods.

  2. Concrete Factory: Concrete applications of the abstract factory interface that are in charge of producing particular kinds of associated goods.

  3. Abstract Product: An interface or abstract class that specifies the common interface for a family of products is known as an abstract product.

  4. Concrete Product: Physical manifestations of the abstract product interface that stand in for particular categories of manufactured goods

Implementing the Abstract Factory Pattern in Java:

Let's delve into a simple implementation of the Abstract Factory pattern in Java:

// Abstract Product interfaces
interface Window {
    void display();
}
interface Button {
    void click();
}
// Concrete Product implementations
class WindowsWindow implements Window {
    @Override
    public void display() {
        System.out.println("Displaying a window for Windows OS.");
    }
}
class WindowsButton implements Button {
    @Override
    public void click() {
        System.out.println("Clicking a button for Windows OS.");
    }
}
class MacOSWindow implements Window {
    @Override
    public void display() {
        System.out.println("Displaying a window for MacOS.");
    }
}
class MacOSButton implements Button {
    @Override
    public void click() {
        System.out.println("Clicking a button for MacOS.");
    }
}
// Abstract Factory interface
interface GUIFactory {
    Window createWindow();
    Button createButton();
}
// Concrete Factory implementations
class WindowsFactory implements GUIFactory {
    @Override
    public Window createWindow() {
        return new WindowsWindow();
    }
    @Override
    public Button createButton() {
        return new WindowsButton();
    }
}
class MacOSFactory implements GUIFactory {
    @Override
    public Window createWindow() {
        return new MacOSWindow();
    }
    @Override
    public Button createButton() {
        return new MacOSButton();
    }
}

Understanding the Implementation:

  • The Window and Button interfaces define the common interface for window and button products.

  • Concrete product implementations (WindowsWindow, WindowsButton, MacOSWindow, MacOSButton) provide specific implementations for Windows and MacOS platforms.

  • The GUIFactory interface declares methods for creating windows and buttons.

  • Concrete factory implementations (WindowsFactory, MacOSFactory) provide platform-specific implementations for creating windows and buttons.

Example Usage of Abstract Factory Pattern:

Let's consider a scenario where we have an application that needs to create GUI components for different operating systems:

class Application {
    private GUIFactory factory;
    public Application(GUIFactory factory) {
        this.factory = factory;
    }
    public void createUI() {
        Window window = factory.createWindow();
        Button button = factory.createButton();
        window.display();
        button.click();
    }
}

Now, we can use the Application class to create GUI components for specific operating systems:

class Main {
    public static void main(String[] args) {
        GUIFactory windowsFactory = new WindowsFactory();
        Application windowsApp = new Application(windowsFactory);
        windowsApp.createUI(); // Creates UI components for Windows OS
        GUIFactory macOSFactory = new MacOSFactory();
        Application macOSApp = new Application(macOSFactory);
        macOSApp.createUI(); // Creates UI components for MacOS
    }
}

In this example, the Application class is decoupled from platform-specific implementations, allowing us to easily switch between different operating systems by changing the factory instance.

Conclusion:

Abstract Factory pattern offers a potent way to group related or dependent things together without having to define their own classes. This style encourages code extensibility, maintainability, and flexibility by defining abstract factories and concrete factory implementations. Whether you're creating system utilities, GUI frameworks, or cross-platform apps, learning the Abstract Factory pattern can greatly improve your software design abilities and enable you to create apps that are more modular and flexible.

4
Subscribe to my newsletter

Read articles from Venu Madhav Emmadi directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Venu Madhav Emmadi
Venu Madhav Emmadi