How to choose between Factory method pattern and Abstract factory pattern
Asked Answered
P

2

15

I know similar questions were asked before. I've been reading a lot about this during the last couple of days and I think I can now understand the differences in terms of design and code flow. What bothers me it's that it seems like both pattern can solve the very same set of problems without a real reason to choose one or another. While I was trying to figure this out by myself, I tried to implement a little example (starting from the one I found on "Head First: Design patterns" book). In this example I tried to solve the same problem twice: one time using only the "Factory method pattern" and the other using the "Abstract Factory pattern". I'll show you the code and then I'll make some comments and the question.

Common interfaces and classes

public interface IDough { }
public interface ISauce { }
public class NYDough : IDough { }
public class NYSauce : ISauce { }
public class KNDough : IDough { }
public class KNSauce : ISauce { }

Pure Factory method pattern

// pure Factory method pattern
public abstract class Pizza
{
    protected IDough Dough { get; set; }
    protected ISauce Sauce { get; set; }
    protected abstract IDough CreateDough();
    protected abstract ISauce CreateSauce();
    public void Prepare()
    {
        Dough = CreateDough();
        Sauce = CreateSauce();
        // do stuff with Dough and Sauce
    }
    public void Bake() { }
    public void Cut() { }
    public void Box() { }
}

public class NYCheesePizza : Pizza
{
    protected override IDough CreateDough()
    {
        return new NYDough();
    }

    protected override ISauce CreateSauce()
    {
        return new NYSauce();
    }
}

public class KNCheesePizza : Pizza
{
    protected override IDough CreateDough()
    {
        return new KNDough();
    }

    protected override ISauce CreateSauce()
    {
        return new KNSauce();
    }

}

public abstract class PizzaStore
{
    public void OrderPizza(string type)
    {
        Pizza pizza = CreatePizza(type);
        pizza.Prepare();
        pizza.Bake();
        pizza.Cut();
        pizza.Box();
    }
    public abstract Pizza CreatePizza(string type);
}

public class NYPizzaStore : PizzaStore
{
    public override Pizza CreatePizza(string type)
    {
        switch (type)
        {
            case "cheese":
                return new NYCheesePizza();
            default:
                return null;
        }
    }
}

public class KNPizzaStore : PizzaStore
{

    public override Pizza CreatePizza(string type)
    {
        switch (type)
        {
            case "cheese":
                return new KNCheesePizza();
            default:
                return null;
        }
    }
}

pure Abstract factory pattern

public interface IIngredientFactory
{
    IDough createDough();
    ISauce createSauce();
}

public class NYIngredientFactory : IIngredientFactory
{
    public IDough createDough()
    {
        return new NYDough();
    }

    public ISauce createSauce()
    {
        return new NYSauce();
    }
}

public class KNIngredientFactory : IIngredientFactory
{
    public IDough createDough()
    {
        return new KNDough();
    }

    public ISauce createSauce()
    {
        return new KNSauce();
    }
}

public class Pizza
{
    IDough Dough { get; set; }
    ISauce Sauce { get; set; }
    IIngredientFactory IngredientFactory { get; set; }

    public Pizza(IIngredientFactory ingredientFactory)
    {
        IngredientFactory = ingredientFactory;
    }

    public void Prepare()
    {
        Dough = IngredientFactory.createDough();
        Sauce = IngredientFactory.createSauce();
    }
    public void Bake() { }
    public void Cut() { }
    public void Box() { }
}

public interface IPizzaFactory
{
    Pizza CreatePizza(string type);
}

public class NYPizzaFactory : IPizzaFactory
{
    public Pizza CreatePizza(string type)
    {
        switch (type)
        {
            case "cheese":
                return new Pizza(new NYIngredientFactory());
            default:
                return null;
        }
    }
}

public class KNPizzaFactory : IPizzaFactory
{
    public Pizza CreatePizza(string type)
    {
        switch (type)
        {
            case "cheese":
                return new Pizza(new KNIngredientFactory());
            default:
                return null;
        }
    }
}

public class PizzaStore
{
    IPizzaFactory PizzaFactory { get; set; }

    public PizzaStore(IPizzaFactory pizzaFactory)
    {
        PizzaFactory = pizzaFactory;
    }

    public void OrderPizza(string type)
    {
        Pizza pizza = PizzaFactory.CreatePizza(type);
        pizza.Prepare();
        pizza.Bake();
        pizza.Cut();
        pizza.Box();
    }
}

If I had used the patterns definitions I would have chosen a "Factory Method Pattern" for the PizzaStore (since it only builds one type of object, Pizza) and the "Abstract Factory Pattern" for the IngredientFactory. Anyway, another design principle, states that you should "favor composition over inheritance" which suggests that I should always use the "Abstract Factory Pattern".

My question is: What are the reasons I should choose the "Factory Method Pattern" in the first place?

EDIT

Let's take a look at the first implementation, the one that uses the Factory method pattern. Jesse van Assen suggested that this is a Template method pattern instead of a Factory method pattern. I'm not convinced it's right. We can break down the first implementation in two parts: the first one that deals with Pizza and the second one that deals with PizzaStore.

1) in the first part Pizza is the client that is dependent on some kind of concrete Dough and Sauce. In order to decouple Pizza from concrete objects I used, within the Pizza class, reference to interfaces only (IDough and ISauce) and I let the Pizza's subclasses decide which concrete Dough and Sauce choose. To me this fits perfectly with the definition of a Factory method pattern:

Define an interface for creating an object, but let the subclasses decide which class to instantiate. The Factory method lets a class defer instantiation to subclasses.

2) in the second part PizzaStore is the client and it's dependent on concrete Pizza. I applied the same principles discussed above.

So, to express better (I hope) what I don't really get is why is said that:

Factory Method pattern is responsible of creating products that belong to one family, while Abstract Factory pattern deals with multiple families of products.

As you see from my examples (provided they are right :-)) you can same stuff with both patterns.

Platto answered 21/1, 2012 at 12:5 Comment(1)
P
9

First, 2 quotes from the GoF design patterns book:

"Abstract Factory is often implemented with factory methods."

"Factory Methods are often called by template methods."

So it's not a question of choosing between Factory Methods and Abstract Factories, because the later can be (and usually is) implemented by the former.

The concept of Abstract Factories (as Amir hinted) is to group the creation of several concrete classes that always go together. In your example they should be the the NY varieties of food components as opposed to the KN ones.

But if you want to allow mix & match (what's wrong with a Pizza with KN dough and NY souce?) then Abstract factories are not your answer. In that case, each Pizza subclass should decide which concrete classes it wishes to create.

If you don't want to allow these kinds of mixings, you should go with Abstract Factories.

Pyelonephritis answered 22/1, 2012 at 10:52 Comment(2)
Sorry, I'm probably to noob to fully grasp what you mean, but I don't see how Abstract factories don't allow what you call "mix&match". You can always define another factory which inherit from IIngredientFactory and that returns the concrete type you want to mix.Platto
Hey, I'm far from being a design patterns expert myself :) Technically you can do anything, but it won't be considered Abstract Factory. The idea of abstract factory is the grouping of concrete objects to families. If what you want is indeed mixing, I wouldn't do it with subclassing at all, since it could result in class bloating. I would probably use some kind of a builder - the Pizza class would specify which type of component it wants (NY dough, KN sauce) with some kind of an identifier, and the builder will be responsible for creating the concrete objects.Pyelonephritis
U
2

If you want a couple of related Factory Methods which should do same kind of decisions, then it is better to group then in Abstract Factories.

I shall say your first implementation is not Factory Method. Factory methods are not abstract, and they have parameters which decide what to instantiate according to them.

Undergraduate answered 21/1, 2012 at 20:42 Comment(4)
Thanks for taking the time to answer. I think that, in Factory Method, methods should be abstract because you want implement them in subclasses. It's a requirement of the pattern. And about the parameters, on the other end, I'm not entirely sure it's a requirement.Platto
The pattern you are using is a template method, not a factory method.Namnama
@JessevanAssen : thank you, you have probably enlightened me and now even Amir's answer make more sense to me. I wasn't aware of the Template method pattern, I'm going to study it a bit more and I'll post something back later.Platto
mm I'm not yet convinced, I made some edit to better explain what I mean. I hope you will have time to read it and share your thoughts. Thank you.Platto

© 2022 - 2024 — McMap. All rights reserved.