views:

136

answers:

6

Take the following classic factory pattern:

public interface IPizza
{
    decimal Price { get; }
}

public class HamAndMushroomPizza : IPizza
{
    decimal IPizza.Price
    {
        get
        {
            return 8.5m;
        }
    }
}
public abstract class PizzaFactory
{
    public abstract IPizza CreatePizza(ItalianPizzaFactory.PizzaType pizzaType);
}

public class ItalianPizzaFactory : PizzaFactory
{
    public enum PizzaType
    {
        HamMushroom,
        Deluxe,
        Hawaiian
    }

    public override IPizza CreatePizza(PizzaType pizzaType)
    {
        switch (pizzaType)
        {
            case PizzaType.HamMushroom:
                return new HamAndMushroomPizza();
            case PizzaType.Hawaiian:
                return new HawaiianPizza();
            default:
                throw new ArgumentException("The pizza type " + pizzaType + " is not recognized.");
        }
    }
}

What if one (or many) of the Concrete Pizzas requires a parameter specific to the concrete implementation at construction. For example, lets say the HamAndMushroom factory requires a parameter called, MushroomType and this parameter would be required to instantiate the object?

+8  A: 

You can add parameters to the creator method(s) of your factory. However, if the number of parameters is getting higher (for me that would be more than 2-3), and especially if some or all of those parameters are optional with reasonable default values, you may consider turning the factory into a Builder instead.

That may be especially appropriate for pizzas, where you usually have the same crust, just with different (combinations) of toppings. A Builder models very closely the common way of ordering e.g. "a pizza with salami, tomatoes, maize and double cheese". OTOH for "predefined" pizzas you may want to define helper factory methods, e.g. createMargaritaPizza or createHawaiiPizza which then internally use the builder to create a pizza with the toppings specific to that kind of pizza.

Péter Török
Also, if the Pizza requires other objects, live the Oven, or specially a StoneOven, i would recommend using and IoC, as these are othogonal objects to the (configureable) toppings ;-)
cRichter
I agree with Peter, Builder pattern would be a more suitable pattern for the scenario described by the OP. He may want to look at a Composite pattern as well for handling the case of a single order of multiple pizza's.
David Yancey
You guys are making me hungry :)
Mike Caron
A: 

You would have to add another CreatePizza() method for that factory class. And that would mean that users of the factory wouldn't be able to create those types of pizzas unless they were specifically using an instance of the HamAndMushroomPizzaFactory class. If they simply have a PizzaFactory reference, they can only call the parameterless version and won't be able to create ham and mushroom pizzas generically.

siride
+1  A: 

You can use reflection:

using System.Reflection;

// ...

public override IPizza CreatePizza(PizzaType pizzaType, params object[] parameters) {
            return (IPizza)
                   Activator.CreateInstance(
                        Assembly
                             .GetExecutingAssembly()
                             .GetType(pizzaType.ToString()),
                        parameters);
        }
Mau
I don't like this approach. If you change the constructor of a class you have to change all calls to the factory (and you will get only runtime errors). This erases the advantage of the factory: hiding construction of objects.
onof
I just vomited `IPizza` all over my keyboard after reading "use reflection". heehee.
Jerod Houghtelling
:-D I know, it defeats the purpose...
Mau
A: 

You could pass a new parameter, such as a Map. And query the properties on each concrete constructor. Then all the methods would have the same signature. However, with this solution, the caller of the constructor has to know the specific properties of the concret constructor...(Coupling)

Pau
A: 
ULysses
I don't think having the pizza description being in a string is the right way to do things. Better would be to have PizzaDefinition be a class that can be subclassed. More specialized pizzas can have more specialized pizza definitions.
siride
@siride: correct, what I did is just specifying an approach. The PizzaDefinition class can be really tough and have some methods that will help you choose from options and so on. But this particular example is still worth considering, since it is very robust and at the same time giving what you may want - a list of available pizzas and a way of creating each one.
ULysses
A: 

You can try something like this:

interface IPizza
{
}

class Pizza1 : IPizza
{
  public Pizza1(Pizza1Parameter p)
  {
  }
}

class Pizza2 : IPizza
{
  public Pizza2(Pizza2Parameter p)
  {
  }
}

interface IPizzaParameter
{
  object Type { get; set; }
}

class Pizza1Parameter : IPizzaParameter
{
  public object Type { get; set; }
}

class Pizza2Parameter : IPizzaParameter
{
  public object Type { get; set; }
}

static class PizzaFactory
{
  public enum PizzaType
  {
    Pizza1,
    Pizza2,
  }

  public static IPizza CreatePizza(PizzaType type, IPizzaParameter param)
  {
    switch (type)
    {
      case PizzaType.Pizza1:
        return new Pizza1(param as Pizza1Parameter);
      case PizzaType.Pizza2:
        return new Pizza2(param as Pizza2Parameter);
    }

    throw new ArgumentException();
  }
}

class Program
{
  static void Main()
  {
    var param1 = new Pizza1Parameter();
    var p1 = PizzaFactory.CreatePizza(PizzaFactory.PizzaType.Pizza1, param1);
  }
}

IMHO concept of factory with implementation specific parameters looks wrong.

Bazurbat