views:

468

answers:

6

Let's say I have an abstract class Drink, and a factory method which chooses the type of Drink (Wine, Beer, etc.) to create at runtime.

Each Drink needs some arguments to properly initialize itself. Some of these are common to all Drinks; for example, they might all require a DrinkConfig argument.

But each Drink may have its own unique requirements too. Maybe Wine needs a Sommelier helper object to initialize itself. Beer doesn't need that, but it may need its own helper objects.

So what should I pass to the factory method? When I call it, I have all the helper objects available, so I could just pass all of them to the factory. But this could end up being a lot of arguments. Is there a better way to design this?

EDIT: Let's assume I can't just create the helper objects in the factory; they're only available from the caller.

+3  A: 

I'd create different overload methods in your factory class.

public class DrinkFactory {

    public static Drink CreateBeer(DrinkConfig config, string hops) {
        return new Beer(config, hops);
    }

    public static Drink CreateWine(DrinkConfig config, string grapes, int temperature) {
        return new Wine(config, grapes, temperature);
    }
}

Edit:

If it's desired to to only have a single method in the Factory class an alternative implementation would be:

public enum DrinksEnum {
    Beer,
    Wine
}

public class DrinkFactory {

    public static Drink CreateDrink(DrinksEnum drinkType, DrinkConfig config) {
        switch(drinkType) {
            case DrinksEnum.Beer:
                return new Beer(config);
            case DrinksEnum.Wine:
                return new Wine(config);
            default:
                throw new ApplicationException("Drink type not recognised.");
        }
    }
}
sipwiz
The signatures are fine. The problem is how do you pass in arguments to your Factory::CreateDrink() (or whatever it is called).
dirkgently
If you wanted to have a CreateDrink method in the factory you could use an enum parameter to specify which drink type you wanted. I don't believe that or the approach above comply with the GoF factory pattern, where the objects get created in the Drink class (I need to check my book tonight), but I find it much more pragmatic and it still maintains the primary benefit of centralising object creation for sub class hierarchies.
sipwiz
After checking my GoF Design Patterns book I'm happy that the example I provided above is how close to how you use a Factory as part of the Abstract Factory design pattern. To conform completely there should be an abstract factory class that DrinkFactory inherits from but for simple cases like this one I normally leave it out. It would be easy enough to refactor DrinkFactory if another concrete factory was required.
sipwiz
Unfortunately, this won't really work for my case. The concrete type is chosen at runtime, so I can't choose to call a specific CreateBeer or CreateWine method. I'm just calling a generic CreateDrink method, which returns a Beer or Wine.
JW
Unfortunately, your edit sample does not provide an answer to his question: what to do with the different signatures?
KoMet
A: 

I am tempted to provide a naive solution where your ingredients are derived from a base class 'DrinkIngredients'. You will have to match the sub-class to be used for a particular drink.

Apparently, you may be tempted to create another factory for the ingredients -- but that would lead to a chicken-and-egg problem.

dirkgently
A: 

Generally, a factory method exists to hide these details. One important question is where the Sommelier comes from -- if all of these other helpers are singleton or can be acquired from a known source, then instantiate the factory the necessary information to go and find them, so that your calling code doesn't need to worry about it.

Also, in many cases, a framework such as Spring would be used to allow you to describe these relationships in a configuration file rather than in code.

If you really need to pass the helpers in at runtime from the calling code, I suggest reading the paper 'Arguments and Results' (http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.50.7565) which describes a common pattern for marshalling complex arguments. Essentially, you would create an intermediate collection of the necessary parameters and pass that to the factory.

Brad B
A: 

The factory method should abstract away the details of how to create values. So you should not be passing helper objects to the factory method, the factory method should be creating the helper object it needs and passing that to the appropriate constructor.

Dave Hinton
A: 

In cases like this, I usually look towards other solutions instead of passing in variables.

For example, in your case - WineFactory needing a Sommelier, so it can construct the appropriate Wine -

This is a great use case for runtime dependency injection. A dependency injection framework of some form would make this very simple, understandable, and just kind of work without needing to have all of these properties passed around.

Reed Copsey
A: 

A Factory should be creating very similar objects in the first place. This means that even though all of these objects are drinks, the factory method may not be appropriate because each drink is simply much different from another.

With that being said, you could instead pass a List of Objects of size equal to the number of properties you want to set. Each object would then represent the value you want to set in the constructor of the appropriate object, in the order in which you want to set these variables. The downside to this is that you have to format a List outside of the factory before making the call, which is somewhat clumsy.

AlbertoPL