Understanding Design Patterns: Unraveling the Factory Pattern in Java

What is Factory Pattern?

A Factory Pattern or Factory Method Pattern says that just defines an interface or abstract class for creating an object but lets the subclasses decide which class to instantiate. It introduces a central "factory" responsible for creating various types of objects without exposing the details of their creation.
The Factory Pattern is like having a specialized factory (class) where various items (objects) are crafted without disclosing the detailed crafting process.

Why Factory Pattern?

Imagine you want to have a custom-made pizza. Instead of making the pizza at home (where you might need to know the details of dough preparation, sauce mixing, and toppings), you simply place an order to a pizza store with your requirements like

1) You want a pizza of the type X / A pizza consists of x inch, y crust, and z spice level.

The store utilizes its pizza factory to handle the complexities, ensuring you get the pizza you want without worrying about the intricate cooking process. Similarly, the Factory Pattern in software development simplifies object creation, making it easier to manage and allowing for future modifications or additions to the product lineup (types of objects) without affecting the ordering process (client code). It promotes a clean and efficient way of creating objects, enhancing flexibility and maintainability in your code.

Implementation

The Factory Pattern involves defining a common interface (Product Interface) for creating objects and allowing subclasses (Concrete Product Classes) to alter the type of objects created. The Creator Interface (Factory) encapsulates the object creation logic. In our example, the KnifeFactory serves as the factory, and the KnifeStore is the client utilizing the factory to create knives.

Code

Let's dive into a practical example using Java to understand the Factory Pattern by implementing a knife ordering system.

// Knife interface with its functionalities
public interface Knife {
    void sharpen();
    void polish();
    void pack();
}

// Concrete product classes
class SteakKnife implements Knife {
    @Override
    public void sharpen() {
        System.out.println("Sharpening a steak knife.");
    }
    @Override
    public void polish() {
        System.out.println("Polishing a steak knife.");
    }
    @Override
    public void pack() {
        System.out.println("Packing a steak knife.");
    }
}
class ChefsKnife implements Knife {
    @Override
    public void sharpen() {
        System.out.println("Sharpening a chef's knife.");
    }
    @Override
    public void polish() {
        System.out.println("Polishing a chef's knife.");
    }
    @Override
    public void pack() {
        System.out.println("Packing a chef's knife.");
    }
}

// Creator interface (Factory)
class KnifeFactory {
    public Knife createKnife(String knifeType) {
        String className = knifeType.substring(0, 1).toUpperCase() + knifeType.substring(1) + "Knife";
        try {
            Class<?> clazz = Class.forName("factory." + className);
            return (Knife) clazz.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            throw new IllegalArgumentException("Invalid knife type: " + knifeType);
        }
    }
}

// The KnifeStore class, the class object to be called for ordering
public class KnifeStore {
    public Knife orderKnife(String knifeType) {
        KnifeFactory knifeFactory = new KnifeFactory();
        Knife knife = knifeFactory.createKnife(knifeType);

        // Common operations for all knives
        knife.sharpen();
        knife.polish();
        knife.pack();
        return knife;
    }
}

Explanation

In this example, we have an Knife interface representing the product and two concrete classes (SteakKnife and ChefsKnife). The KnifeFactory encapsulates the creation logic, dynamically instantiating the appropriate concrete class based on the input. The KnifeStore acts as the client, utilizing the factory to create knives while performing common operations on them.

I would prompt you to refractory the code a bit for practice purposes and use your client code to call KnifeStore to order your favourite type of knife.

If you are new to design patterns find out my article Understanding Design Patterns: A Beginner's Guide. Stay tuned for the next article on the Factory method pattern.