Factory Method pattern

Factory Method Pattern definition

All factory patterns encapsulate object creation. The Factory Method Pattern encapsulates object creation by letting subclasses decide what objects to create.

It defines an interface for creating an object (Pizza), but lets subclasses (NYStyleCheesePizza, NYStylePepperoniPizza, NYStyleVeggiePizza, ChicagoStyleCheesePizza, ChicagoStylePepperoniPizza, ChicagoStyleVeggiePizza) define which class to instantiate. Factory Method lets a class defer instantiation to subclasses.

It gives us a way to encapsulate the instantiation of concrete types.

To create objects using Factory Method, you need to extend a class (PizzaStore) and provide an implementation for a factory method (createPizza). What does the factory method do? It creates objects. The whole point of Factory Method pattern is that you are using a subclass (NYPizzaStore and ChicagoPizzaStore) to do your creation for you. In that way, the clients only need to know the abstract type (PizzaStore) they are using, and the subclass (NYPizzaStore and ChicagoPizzaStore) worries about the concrete type. In other words, the clients are decoupled from the concrete types.

Look at the example. NYPizzaStore అనే class దానికి సంబందించిన subclasses (NYStyleCheesePizza, NYStylePepperoniPizza, NYStyleVeggiePizza) లో ఒక దానిని instantiate చేసి ఇస్తుంది. అన్ని subclasses లో ఒకటి మాత్రమే instantiate అవుతుంది. అన్ని subclasses ఎందుకు? ఎందుకంటే inheritance వాడుతున్నాము. Factory Method creates objects through inheritance.

When to use this pattern?

To decouple your client code from concrete classes you need to instantiate, or if you don’t know ahead of time all the concrete classes you are going to need. To use it, just subclass it and implement the factory method.

Franchising

Now, you want to set-up pizza store in New York, Chicago and California.

One approach is take out SimplePizzaFactory and create three different factories - NYPizzaFactory, ChicagoPizzaFactory and CaliforniaPizzaFactory. Then, we just compose the PizzaStore with the appropriate factory and a franchise is good to go.

NYPizzaFactory nyFactory = new NYPizzaFactory();
PizzaStore nyStore = new PizzaStore(nyFactory);
nyStore.orderPizza("Veggie");

ChicagoPizzaFactory chicagoFactory = new ChicagoPizzaFactory();
PizzaStore chicagoStore = new PizzaStore(chicagoFactory);
chicagoStore.orderPizza("Pepperoni");

CaliforniaPizzaFactory californiaFactory = new CaliforniaPizzaFactory();
PizzaStore californiaStore = new PizzaStore(californiaFactory);
californiaStore.orderPizza("Cheese");

Quality control

You found out that the franchises were using your factory to create pizzas, but starting to employ their own home-grown procedures for the rest of the process: they are baking things differently, they are forgetting to cut the pizza and they are using third-party boxes.

You want to change the design to create a framework that ties the store and the pizza creation together, yet still allow things to remain flexible.

In our early code, before the SimplePizzaFactory, we had the pizza-making code tied to the PizzaStore. But it wasn’t flexible. How would we change things?

Framework

There is a way to localize all the pizza-making activities to the PizzaStore class, and yet give the franchises freedom to have their own regional style.

Put the createPizza() method back into PizzaStore, but this time as an abstract method, and then create a PizzaStore subclass for each regional style.

A factory method handles object creation and encapsulates it in a subclass. This decouples the client code in the superclass from the object creation code in the subclass.

public abstract class PizzaStore {

        protected abstract Pizza createPizza(String item);

        public Pizza orderPizza(String type) {
                Pizza pizza = createPizza(type);
                System.out.println("--- Making a " + pizza.getName() + " ---");
                pizza.prepare();
                pizza.bake();
                pizza.cut();
                pizza.box();
                return pizza;
        }
}

Allowing the subclasses to decide

You want to ensure that the (well-honed and time tested) code in the orderPizza() method is consistent across all franchises.

What varies among the regional PizzaStores is the style of pizzas they make - New York Pizza has thin crust, Chicago Pizza has thick, and so on - and we are going to push all these variations into the createPizza() method and make it responsible for creating the right kind of pizza. The way we do this is by letting each subclass of PizzaStore define what the createPizza() method looks like. So, we will have a number of concrete subclasses of PizzaStore, each with its own pizza variations, all fitting within the PizzaStore framework and still making use of the well-tuned orderPizza() method.

Each concrete subclass of PizzaStore has implementation for the abstract method createPizza()

Being a franchise has its benefits. You get all the PizzaStore functionality for free. All the regional stores need to do is, subclass PizzaStore and supply a createPizza() method that implements their style of Pizza.

NYPizzaStore

public class NYPizzaStore extends PizzaStore {

        Pizza createPizza(String item) {
                if (item.equals("cheese")) {
                        return new NYStyleCheesePizza();
                } else if (item.equals("veggie")) {
                        return new NYStyleVeggiePizza();
                } else if (item.equals("clam")) {
                        return new NYStyleClamPizza();
                } else if (item.equals("pepperoni")) {
                        return new NYStylePepperoniPizza();
                } else return null;
        }
}

ChicagoPizzaStore

public class ChicagoPizzaStore extends PizzaStore {

        Pizza createPizza(String item) {
                if (item.equals("cheese")) {
                        return new ChicagoStyleCheesePizza();
                } else if (item.equals("veggie")) {
                        return new ChicagoStyleVeggiePizza();
                } else if (item.equals("clam")) {
                        return new ChicagoStyleClamPizza();
                } else if (item.equals("pepperoni")) {
                        return new ChicagoStylePepperoniPizza();
                } else return null;
        }
}

The orderPizza() method does a lot of things with a Pizza object (like prepare, bake, cut, box), but because Pizza is abstract, orderPizza() has no idea what real concrete classes are involved. It is decoupled.

When orderPizza() calls createPizza(), one of the subclasses will be called into action to create a pizza. Which kind of pizza will be made? That depends upon the choice of the PizzaStore you order from.

Is there a real-time decision that subclasses make? No, but from the perspective of orderPizza(), if you chose a NYStylePizzaStore, that subclass gets to determine which pizza is made. So the subclasses are not deciding anything - it is you that is deciding by chosing which store you want - but the subclasses do determine which kind of pizza gets made.

Pizza class

import java.util.ArrayList;

public abstract class Pizza {
        String name;
        String dough;
        String sauce;
        ArrayList<String> toppings = new ArrayList<String>();

        void prepare() {
                System.out.println("Prepare " + name);
                System.out.println("Tossing dough...");
                System.out.println("Adding sauce...");
                System.out.println("Adding toppings: ");
                for (String topping : toppings) {
                        System.out.println("   " + topping);
                }
        }

        void bake() {
                System.out.println("Bake for 25 minutes at 350");
        }

        void cut() {
                System.out.println("Cut the pizza into diagonal slices");
        }

        void box() {
                System.out.println("Place pizza in official PizzaStore box");
        }

        public String getName() {
                return name;
        }

        public String toString() {
                StringBuffer display = new StringBuffer();
                display.append("---- " + name + " ----\n");
                display.append(dough + "\n");
                display.append(sauce + "\n");
                for (String topping : toppings) {
                        display.append(topping + "\n");
                }
                return display.toString();
        }
}

Some concrete subclasses:

NYStyleCheesePizza

public class NYStyleCheesePizza extends Pizza {

        public NYStyleCheesePizza() {
                name = "NY Style Sauce and Cheese Pizza";
                dough = "Thin Crust Dough";
                sauce = "Marinara Sauce";

                toppings.add("Grated Reggiano Cheese");
        }
}

ChicagoStyleCheesePizza

public class ChicagoStyleCheesePizza extends Pizza {

        public ChicagoStyleCheesePizza() {
                name = "Chicago Style Deep Dish Cheese Pizza";
                dough = "Extra Thick Crust Dough";
                sauce = "Plum Tomato Sauce";

                toppings.add("Shredded Mozzarella Cheese");
        }

        void cut() {
                System.out.println("Cutting the pizza into square slices");
        }
}

Testing the code:

public class PizzaTestDrive {

        public static void main(String[] args) {
                PizzaStore nyStore = new NYPizzaStore();
                PizzaStore chicagoStore = new ChicagoPizzaStore();

                Pizza pizza = nyStore.orderPizza("cheese");
                System.out.println("Ethan ordered a " + pizza.getName() + "\n");

                pizza = chicagoStore.orderPizza("cheese");
                System.out.println("Joel ordered a " + pizza.getName() + "\n");

                pizza = nyStore.orderPizza("clam");
                System.out.println("Ethan ordered a " + pizza.getName() + "\n");

                pizza = chicagoStore.orderPizza("clam");
                System.out.println("Joel ordered a " + pizza.getName() + "\n");

                pizza = nyStore.orderPizza("pepperoni");
                System.out.println("Ethan ordered a " + pizza.getName() + "\n");

                pizza = chicagoStore.orderPizza("pepperoni");
                System.out.println("Joel ordered a " + pizza.getName() + "\n");

                pizza = nyStore.orderPizza("veggie");
                System.out.println("Ethan ordered a " + pizza.getName() + "\n");

                pizza = chicagoStore.orderPizza("veggie");
                System.out.println("Joel ordered a " + pizza.getName() + "\n");
        }
}

Are NYPizzaStore and ChicagoPizzaStore created using Simple Factory or Factory Method? Even though their implementation looks similar to Simple Factory, they are extending a class that has defined createPizza() as an abstract method. It is up to each store to define the behavior of the createPizza() method. In SimpleFactory, the factory is another object that is composed with the PizzaStore.

Class diagram

┌──────────────────┐                         ┌────────────────────┐
│                  │                         │                    │
│    Product       │                         │   Creator          │
├──────────────────┤                         │                    │
│                  │                         ├────────────────────┤
│                  │                         │                    │
│                  │                         │   factoryMethod()  │
│                  │                         │                    │
└──────────────────┘                         │                    │
          ▲                                  │   anOperation()    │
          │                                  │                    │
          │                                  └────────────▲───────┘
          │                                               │
          │                                               │
┌─────────┴─────────┐                         ┌───────────┴─────────┐
│                   │                         │                     │
│  ConcreteProduct  │◄────────────────────────┤  ConcreteCreator    │
│                   │                         │                     │
├───────────────────┤                         ├─────────────────────┤
│                   │                         │                     │
│                   │                         │  factoryMethod()    │
└───────────────────┘                         │                     │
                                              └─────────────────────┘

Things to note from the class diagram:

  1. The Creator is a class that contains the implementation for all of the methods to manipulate products, except for the factory method.

  2. The abstract factoryMethod() is what all Creator subclasses must implement.

  3. The ConcreteCreator implements the factoryMethod(), which is the method that actually produces products.

  4. The ConcreteCreator is responsible for creating one or more concrete products. It is the only class that has the knowledge of how to create these products.

  5. All products must implement the same interface so that the classes that use the products can refer to the interface, not the concrete class.

  6. The abstract Creator gives you an interface with a method for creating objects - also known as the “factory method”.

  7. Any other methods implemented in the abstract Creator are written to operate on products produced by the factory method.

  8. Only subclasses actually implement the factory method and create products.

Developers often say that the Factory Method lets subclasses decide which classes to instantiate. They say “decide” not because the pattern allows subclasses themselves to decide at runtime, but because the creator class is written without knowledge of the actual products that will be created, which is decided purely by the choice of the subclass that is used.

Are there advantages to using Factory Method Pattern when you have only one ConcreteCreator? Yes. Because you are decoupling the implementation of the product from its use. If you add additional products or change a product’s implementation, it will not affect your Creator (because the Creator is not tightly coupled to any ConcreteProduct).

Are the factoryMethod() and the Creator always abstract? No. You can define a default factory method to produce some concrete product. Then you always have a means of creating products even if there are no subclasses of the Creator.

NYPizzaStore and ChicagoPizzaStore make multiple kinds of pizzas based on the type passed in. Do all concrete creators make multiple products, or do they sometimes make just one? They could make just one. With NYPizzaStore and ChicagoPizzaStore, we are looking at what is known as the parameterized factory method. They can make more than one object based on a parameter passed in. However, sometimes a factory produces just one object and is not parameterized. Both are valid forms of the pattern.

Type safe parameterized factory method: NYPizzaStore and ChicagoPizzaStore are not type-safe. If you pass in an invalid type and ask them for a Pizza, that will cause a runtime error. You can make them type-safe by creating objects that represent the parameter types, use static constants, or use enums.

Following the Dependency Inversion Principle

The Dependency Inversion Principle

Applying Dependency Inversion Principle to PizzaStore:

According to Dependency Inversion Principle, we should write our code so that we are depending on abstractions, not concrete classes. That goes for both our high-level modules and our low-level modules.

Before applying the Factory Method pattern, the main problem with PizzaStore is that it depends on every type of pizza because it actually instantiates concrete types in its orderPizza() method.

While we have created an abstraction, Pizza, we are nevertheless creating concrete Pizzas in the code. So we don’t get a lot of leverage out of this abstraction.

How can we get those instantiations out of the orderPizza() method? By using the Factory Method pattern. After applying the Factory Method pattern, our high-level component, the PizzaStore, and our low-level components, the pizzas, both depend on Pizza, the abstraction. Factory Method is not the only technique for adhering to the Dependency Inversion principle, but it is one of the more powerful ones.


Links to this note