views:

53

answers:

2

I have a factory-type class that creates its products based on the traits of another object. This means that I need a reference to the input object somewhere. I am planning to either:

A) Define the input object as a property and set it in a custom init method. So the factory's owner calls "initWithObject:", then calls "createProduct".

B) Define the factory's creation methods so that they take in the input object as an argument. So the factory's owner inits normally and then calls "createProductWithObject:".

All else equal, is one of these methods preferable to the other from an overall design standpoint? Method A makes things simpler for me since I don't have to make every method accept an input, but I'd like to be sure that I'm not overlooking anything.

Thanks!

+1  A: 

I think it completely depends on how you intend to use your factory class. If this were a pizza factory, you might want to use option A in a scenario where the style of all pizzas (the products) depends on the initial value.

PizzaFactory *factory = [[PizzaFactory alloc] initWithStyle:NewYorkStylePizza];

On the other hand, the individual pizzas can depend on something that other pizzas from the same factory don't.

Pizza *meatLovers = [factory createPizzaWithToppings:toppings];

In this case, both options are being used. You'll just need to decide what makes the most sense for your needs.

Cory Kilger
A: 

I base my choice of which to do on whether the class must have a value for a property, or can go without it.

Borrowing Cory Kilger's idea of food-based examples, let's consider a Hamburger class. The instance must have a bun and a patty:

Patty *patty = [BeefPatty patty];
Bun *bun = [WhiteBun bun];
Hamburger *burger = [Hamburger burgerWithSinglePatty:patty bun:bun];
//Instantiation without convenience factory method
Hamburger *burger = [[[Hamburger alloc] initWithSinglePatty:patty bun:bun] autorelease];

But can have any number of toppings:

[burger addToppingsObject:[LeafLettuce lettuce]];
[burger addToppingsObject:[TomatoSlice sliceWithThickness:1.0 /*cm*/]];
[burger addToppingsObject:[CheddarCheese slice]];

Or none at all:

//No toppings—just meat in a bun.

A steak doesn't have any required elements, so you would instantiate it with no arguments:

Steak *steak = [NewYorkSteak steak];
//Instantiation without convenience factory method
Steak *steak = [[[NewYorkSteak alloc] init] autorelease];

But you could add values for optional elements if you want to:

steak.sauce = [userSelectedSauce sauceInAmount:2.0 /*ml*/];
Peter Hosey