************************
the enemy
************************
Meanwhile, back at the PizzaStore...
The design for the PizzaStore is really shaping up: it’s got a flexible framework and it does a good job of adhering to design principles.
We have met the enemy, and they are us
We’ve got the same product families (dough, sauce, cheese, veggies, meats) but different implementations based on region.
Families of ingredients...
New York uses one set of ingredients and Chicago another.
Given the popularity of Objectville Pizza it won’t be long before you also need to ship another set of regional ingredients to California, and what’s next? Seattle?
Each family consists of a type of dough, a type of sauce, a type of cheese, and a seafood topping (along with a few more we haven’t shown, like veggies and spices).
Building the ingredient factories
Let’s start by defining an interface for the factory that is going to create all our ingredients:
Here’s what we’re going to do:
- Build a factory for each region. To do this, you’ll create a subclass of PizzaIngredientFactory that implements each create method
- Implement a set of ingredient classes to be used with the factory, like ReggianoCheese, RedPeppers, and ThickCrustDough. These classes can be shared among regions where appropriate.
- Then we still need to hook all this up by working our new ingredient factories into our old PizzaStore code.
Building the New York ingredient factory
The NY ingredient factory implements the interface for all ingredient factories.
For each ingredient in the ingredient family, we create the New York version.
Reworking the pizzas...
Reworking the pizzas, continued...
When we wrote the Factory Method code, we had a NYCheesePizza and a ChicagoCheesePizza class.
If you look at the two classes, the only thing that differs is the use of regional ingredients.
The pizzas are made just the same (dough + sauce + cheese).
The same goes for the other pizzas: Veggie, Clam, and so on.
They all follow the same preparation steps; they just have different ingredients.
So, what you’ll see is that we really don’t need two classes for each pizza; the ingredient factory is going to handle the regional differences for us.
To make a pizza now, we need a factory to provide the ingredients.
So each Pizza class gets a factory passed into its constructor, and it’s stored in an instance variable.
The prepare() method steps through creating a cheese pizza, and each time it needs an ingredient, it asks the factory to produce it.
Revisiting our pizza stores
The NY Store is composed with a NY pizza ingredient factory.
This will be used to produce the ingredients for all NY style pizzas.
We now pass each pizza the factory that should be used to produce its ingredients.
For each type of Pizza, we instantiate a new Pizza and give it the factory it needs to get its ingredients.
What have we done?
That was quite a series of code changes; what exactly did we do?
We provided a means of creating a family of ingredients for pizzas by introducing a new type of factory called an Abstract Factory.
An Abstract Factory gives us an interface for creating a family of products.
By writing code that uses this interface, we decouple our code from the actual factory that creates the products.
That allows us to implement a variety of factories that produce products meant for different contexts – such as different regions, different operating systems, or different look and feels.
Because our code is decoupled from the actual products, we can substitute different factories to get different behaviors (like
getting marinara instead of plum tomatoes).