Factory Method Pattern

The Factory Method Pattern is a creational design pattern that defines an interface for creating an object, but lets subclasses decide which class to instantiate. This pattern delegates the instantiation logic to subclasses, allowing them to create different types of products.
Key Principles of the Factory Method Pattern
- Delegation of Object Creation: The Factory Method Pattern shifts the responsibility of object creation from a base class to its subclasses, following the principle of “programming to an interface, not an implementation.”
- Single Responsibility Principle: Each subclass (factory) is responsible for creating specific types of objects. This keeps the code modular and manageable.
- Flexibility and Extensibility: Since subclasses decide which object to create, adding new types is simple; you only need to create a new subclass to produce a new type of object without modifying existing code.
Factory Method Pattern in the Burger Example
In the burger example, we use a hierarchy of restaurant types to create different kinds of burgers. Each restaurant represents a specific factory, encapsulating the creation of a particular type of Burger
. This approach illustrates the Factory Method Pattern well because each subclass overrides the factory method (createBurger()
) to produce a specific burger type.
Structure
- Product Interface (
Burger
): This defines the contract (i.e.,prepare
method) that all burger types will follow. - Concrete Product Classes (
FalafelBurger
,QuinoaBurger
,TofuBurger
): These implement theBurger
interface, each defining a uniqueprepare
method to differentiate the type of burger. - Creator (
Restaurant
Class): This is an abstract class that contains theorderBurger()
method, which callscreateBurger()
to get aBurger
instance. This method acts as a template method for preparing a burger, handling the common preparation process. - Concrete Creators (
FalaFelRestaurant
,QuinoaRestaurant
,TofuRestaurant
): These subclasses extendRestaurant
and implement thecreateBurger()
factory method to instantiate specific types of burgers.
Explanation of Flow
- Client Code: When the client requests a burger through
orderBurger()
, it doesn't know or care about the specific type of burger being created. - Factory Method (
createBurger()
): The abstractRestaurant
class contains an abstractcreateBurger()
method. Each subclass implements this method to create a specific burger type. - Dynamic Behavior: When a
FalaFelRestaurant
instance is asked toorderBurger()
, thecreateBurger()
method creates aFalafelBurger
. Similarly, aQuinoaRestaurant
creates aQuinoaBurger
, and so on.
Advantages of Using Factory Method Pattern in the Burger Example
- Code Reusability: The
Restaurant
class’sorderBurger()
method handles the preparation process in one place, making it reusable across different burger types. - Easy Extensibility: Adding a new type of burger only requires adding a new
Burger
subclass and a newRestaurant
subclass that implementscreateBurger()
to return the new burger type. - Encapsulation: The client code does not need to know the specific burger classes or the creation logic, which is hidden within each
Restaurant
subclass.
Burger
Interface
public interface Burger {
void prepare();
}
Concrete Burger Classes
public class FalafelBurger implements Burger {
@Override
public void prepare() {
System.out.println("Preparing Falafel Burger");
}
}
public class QuinoaBurger implements Burger {
@Override
public void prepare() {
System.out.println("Preparing Quinoa Burger");
}
}
public class TofuBurger implements Burger {
@Override
public void prepare() {
System.out.println("Preparing Tofu Burger");
}
}
Restaurant
Abstract Class
public abstract class Restaurant {
// Template method
public Burger orderBurger() {
Burger burger = createBurger(); // Factory Method
burger.prepare();
return burger;
}
// Factory Method
public abstract Burger createBurger();
}
Concrete Restaurant Classes
public class FalaFelRestaurant extends Restaurant {
@Override
public Burger createBurger() {
return new FalafelBurger();
}
}
public class QuinoaRestaurant extends Restaurant {
@Override
public Burger createBurger() {
return new QuinoaBurger();
}
}
public class TofuRestaurant extends Restaurant {
@Override
public Burger createBurger() {
return new TofuBurger();
}
}
Main Class (Client Code)
public class Main {
public static void main(String[] args) {
Restaurant falafelRestaurant = new FalaFelRestaurant();
Restaurant quinoaRestaurant = new QuinoaRestaurant();
Restaurant tofuRestaurant = new TofuRestaurant();
falafelRestaurant.orderBurger(); // Output: Preparing Falafel Burger
quinoaRestaurant.orderBurger(); // Output: Preparing Quinoa Burger
tofuRestaurant.orderBurger(); // Output: Preparing Tofu Burger
}
}
When to Use the Factory Method Pattern
The Factory Method Pattern is suitable when:
- You want to delegate the responsibility of instantiating objects to subclasses.
- You need to add new types of objects easily, without modifying existing code.
- You have a scenario where object creation logic might change frequently based on context.
In the burger example, this approach allows you to add more types of burgers (e.g., VeggieBurger
, BlackBeanBurger
) by simply creating a new Restaurant
subclass, without changing the core Restaurant
or Burger
code. This flexibility, combined with the ability to encapsulate instantiation logic, is what makes the Factory Method Pattern a powerful choice in scenarios like this.