What are the disadvantages of a “simple factory”

design-patternsfactoryfactory-method

I am reading the book »Head First Design Patterns« from O'Reilly. Before explaining the Factory Method Pattern, they introduce a Simple Factory first.

They are using the example of a pizzeria. In a first step they show the problem:

Pizza orderPizza(string type) {
  Pizza pizza;
  if (type.equals("Pepperoni") { pizza = new PepperoniPizza(); }
  else if (type.equals("Salmon") { pizza = new SalmonPizza(); }
  // ... and so on

  pizza.Prepare();
  pizza.Bake();
  pizza.Cut();
  pizza.Pack();
  return pizza;
}

The obvious problem is that you had to change Pizzeria whenever you add or remove a Pizza.
Hence they introduce a "Simple Factory Idiom" first. They move the creating part into a class "SimplePizzaFactory". Now you don't need to modify Pizzeria anymore when adding or removing a Pizza.

Then they say that this approach isn't that good, when you have more than one pizzeria (in several towns).
I don't really understand their reasoning. They give the following example code and then they say that each pizzeria wouldn't be using the procedure as implemented above but were using different methods in order to "bake", "cut" and "pack" the pizza.

BerlinPizzaFactory berlinFactory = new BerlinPizzaFactory();
Pizzeria berlinPizzeria = new Pizzeria(berlinFactory);
berlinPizza.Order("Pepperoni");

Instead of using the Simple Factory, they suggest using the Factory Method Pattern.

First, I don't see why the BerlinPizzeria is supposed to not using the procedure. It's still a Pizzeria and when you call Order, you're using the same procedure.

My best guess is that they are implying that you are able to implement, let's say, a cafeteria (I'm deliberately using something entirely different to make my point) and use the factory (as it is independent of the pizzeria) and prepare the pizza in a way you want to.

But even when using the Factory Method Pattern, nobody forces you to use the default procedure. It's even simpler to "hide" that you're doing it differently. Their code examples are given in Java and Java methods are virtual by default. So I would be able to implement BerlinPizzeria and override Order (or had to explicitly declare the method as final). The client, however, wouldn't notice that my BerlinPizzeria is doing things differently.

In conclusion I don't see any significant difference between a Simple Factory and the Factory Method Pattern. The only advantage of the Factory Method Pattern I'm seeing is that you would save a few classes (namely, the 'outsourced' factories).

So, what is really the disadvantages of a Simple Factory and why isn't is a good idea to 'outsource' the creating part?
Or what is really the advantage of the Factory Method pattern and why is it a good idea to force the creating part being implemented in the subclass?

Best Answer

Let's have a look at the Simple Factory The intention for the Simple Factory is straightforward: It produces instances of concrete classes. Sticking to your example: you have a Pizzeria with methods like order():

public enum Pizzas {
    QUADDROSTAGGIONI, SALMONE, ROMANA
}

public class Pizzeria {        
    public static Pizza order(Pizzas type){
        switch(type){
            case ROMANA: return new Romana();
            case SALMONE: return new Salmone();
            case QUADDROSTAGGIONI: return new Quaddrostaggioni();
            default: throw new NoRecipeException();
        }
    }

}

This is really a simple case of a factory. If you decide, that you wanted a Salmone-Pizza, you simply order an instance like this:

  Pizza salmone=Pizzeria.order(Pizzas.SALMONE);

The advantage: you separated the use of Pizzas from its creation. You only want Pizza, independend of its creation process. You could enjoy Pizza without baking one yourself.

The downside: If you have several subtypes of the given flavours of Pizza (e.g. ROMANABerlinStyle, ROMANAHamburgStyle) you have two choices.

1) to add the new kind to the existing Pizzeria. That clutters your Pizzeria and you have to deal with several flavours, which aren't that different (e.g. ROMANABerlinStyle adds garlic and ROMANAHamburgStyle adds wild garlic) in one Pizzeria. Besides: it is of no advantage knowing, that you consume a berlinstyle or hamburgstyle pizza. You only wanted some well defined kind of Pizza from the Pizzeria.

2) you abstract one step further: you separate the individual flavours of the given kinds QUADDROSTAGGIONI, SALMONE, ROMANA into independend factories. That means, the BerlinPizzeria knows its recipe and so does the HamburgPizzeria.

You are now allowed to have many different Pizzerias which could have their own recipes:

The traditional Pizzeria from above

public class PizzeriaImpl implements Pizzeria {

    public Pizza order(Pizzas type){
        switch(type){
            case ROMANA: return new Romana();
            case SALMONE: return new Salmone();
            case QUADDROSTAGGIONI: return new Quaddrostaggioni();
            default: throw new NoRecipeException();
        }
    }

}

and a berlinstyle one

public class BerlinPizzeriaImpl implements Pizzeria{

    public Pizza order(Pizzas type){
        switch(type){
            case ROMANA: return new BerlinRomana();
            case SALMONE: return new BerlinSalmone();
            case QUADDROSTAGGIONI: return new Quaddrostaggioni();
            default: throw new NoRecipeException();
        }
    }

}

Now you are able to start out your franchise:

public class PizzaFranchiseImpl implements Pizzeria{

    Pizzeria p;

    public  Pizza order(Pizzas type){
        return p.order(type);
    }

    public PizzaFranchiseImpl(Pizzeria p){
        this.p=p;
    };

}

If you are in Berlin you simply inject your BerlinPizzeriaImpl.

The advantage: you are free to decide which flavour you support in each city (BerlinRomana, BerlinSalmone, traditional Quadrostaggioni). And this is completely transparent to your customer/consumer.

This follows the OpenClosed-principle: Your Pizzeria is closed, as you have a fixed set of pizzas, which you deliver (QUADDROSTAGGIONI, SALMONE, ROMANA), but you are open for new flavours, e.g. berlinstyle.

Further: each pizzeria itself is devoted to one style of pizza. The BerlinPizzeriaImpl doesn't care how to do a traditional Romana (Single Responsibilty Principle).

Last but not least, you decouple different pizza styles from the franchise. The franchise needs only to know how to handle orders.

EDIT: I substituted the dead body with a delicious but dead fish :D