Chapter 4. Baking with OO Goodness: The Factory Pattern
Get ready to bake some loosely coupled OO designs. There is more to making objects than just using the new operator. You’ll learn that instantiation is an activity that shouldn’t always be done in public and can often lead to coupling problems. And we don’t want that, do we? Find out how Factory Patterns can help save you from embarrassing dependencies.
When you see “new,” think “concrete.”
Yes, when you use the new operator you are certainly instantiating a concrete class, so that’s definitely an implementation and not an interface. And you make a good observation: that tying your code to a concrete class can make it more fragile and less flexible.
When we have a whole set of related concrete classes, often we end up writing code like this:
Here we’ve got several concrete classes being instantiated, and the decision of which to instantiate is made at runtime depending on some set of conditions.
When you see code like this, you know that when it comes time for changes or extensions, you’ll have to reopen this code and examine what needs to be added (or deleted). Often this kind of code ends up in several parts of the application, making maintenance and updates more difficult and error-prone.
What’s wrong with “new”?
Technically there’s nothing wrong with the new operator. After all, it’s a fundamental part of most modern object-oriented languages. The real culprit is our old friend CHANGE and how change impacts our use of new.
By coding to an interface, you know you can insulate yourself from many of the changes that might happen to a system down the road. Why? If your code is written to an interface, then it will work with any new classes implementing that interface through polymorphism. However, when you have code that makes use of lots of concrete classes, you’re looking for trouble because that code may have to be changed as new concrete classes are added. So, in other words, your code will not be “closed for modification.” To extend your code with new concrete types, you’ll have to reopen it.
Note
Remember that designs should be “open for extension but closed for modification.” See Chapter 3 for a review.
So what can you do? It’s times like these that you can fall back on OO design principles to look for clues. Remember, our first principle deals with change and guides us to identify the aspects that vary and separate them from what stays the same.
Identifying the aspects that vary
Let’s say you have a pizza shop, and as a cutting-edge pizza store owner in Objectville you might end up writing some code like this:
But you need more than one type of pizza...
So then you’d add some code that determines the appropriate type of pizza and then goes about making the pizza:
But the pressure is on to add more pizza types
You realize that all of your competitors have added a couple of trendy pizzas to their menus: the Clam Pizza and the Veggie Pizza. Obviously you need to keep up with the competition, so you’ll add these items to your menu. And you haven’t been selling many Greek pizzas lately, so you decide to take that off the menu:
Clearly, dealing with which concrete class is instantiated is really messing up our orderPizza() method and preventing it from being closed for modification. But now that we know what is varying and what isn’t, it’s probably time to encapsulate it.
Encapsulating object creation
So now we know we’d be better off moving the object creation out of the orderPizza() method. But how? Well, what we’re going to do is take the creation code and move it out into another object that is only going to be concerned with creating pizzas.
We’ve got a name for this new object: we call it a Factory.
Factories handle the details of object creation. Once we have a SimplePizzaFactory, our orderPizza() method becomes a client of that object. Anytime it needs a pizza, it asks the pizza factory to make one. Gone are the days when the orderPizza() method needs to know about Greek versus Clam pizzas. Now the orderPizza() method just cares that it gets a pizza that implements the Pizza interface so that it can call prepare(), bake(), cut(), and box().
We’ve still got a few details to fill in here; for instance, what does the orderPizza() method replace its creation code with? Let’s implement a simple factory for the pizza store and find out...
there are no Dumb Questions
Q: What’s the advantage of this? It looks like we’re just pushing the problem off to another object.
A: One thing to remember is that the SimplePizzaFactory may have many clients. We’ve only seen the orderPizza() method; however, there may be a PizzaShopMenu class that uses the factory to get pizzas for their current description and price. We might also have a HomeDelivery class that handles pizzas in a different way than our PizzaShop class but is also a client of the factory.
So, by encapsulating the pizza creating in one class, we now have only one place to make modifications when the implementation changes.
And, don’t forget, we’re also just about to remove the concrete instantiations from our client code.
Q: I’ve seen a similar design where a factory like this is defined as a static method. What’s the difference?
A: Defining a simple factory as a static method is a common technique and is often called a static factory. Why use a static method? Because you don’t need to instantiate an object to make use of the create method. But it also has the disadvantage that you can’t subclass and change the behavior of the create method.
The Simple Factory defined
The Simple Factory isn’t actually a Design Pattern; it’s more of a programming idiom. But it is commonly used, so we’ll give it a Head First Pattern Honorable Mention. Some developers do mistake this idiom for the Factory Pattern, but the next time that happens you can subtly show you know your stuff; just don’t strut as you educate them on the distinction.
Just because Simple Factory isn’t a REAL pattern doesn’t mean we shouldn’t check out how it’s put together. Let’s take a look at the class diagram of our new Pizza Store:
Think of Simple Factory as a warm-up. Next, we’ll explore two heavy-duty patterns that are both factories. But don’t worry, there’s more pizza to come!
Note
*Just another reminder: in design patterns, the phrase “implement an interface” does NOT always mean “write a class that implements a Java interface, by using the ‘implements’ keyword in the class declaration.” In the general use of the phrase, a concrete class implementing a method from a supertype (which could be a abstract class OR interface) is still considered to be “implementing the interface” of that supertype.
Franchising the pizza store
Your Objectville Pizza Store has done so well that you’ve trounced the competition and now everyone wants a Pizza Store in their own neighborhood. As the franchiser, you want to ensure the quality of the franchise operations and so you want them to use your time-tested code.
But what about regional differences? Each franchise might want to offer different styles of pizzas (New York, Chicago, and California, to name a few), depending on where the franchise store is located and the tastes of the local pizza connoisseurs.
Note
Yes, different areas of the US serve very different styles of pizza—from the deep-dish pizzas of Chicago, to the thin crust of New York, to the cracker-like pizza of California (some would say topped with fruits and nuts).
We’ve seen one approach...
If we take out SimplePizzaFactory and create three different factories—NYPizzaFactory, ChicagoPizzaFactory, and CaliforniaPizzaFactory—then we can just compose the PizzaStore with the appropriate factory and a franchise is good to go. That’s one approach.
Let’s see what that would look like...
But you’d like a little more quality control...
So you test-marketed the SimpleFactory idea, and what you found was that the franchises were using your factory to create pizzas, but starting to employ their own home-grown procedures for the rest of the process: they’d bake things a little differently, they’d forget to cut the pizza, and they’d use third-party boxes.
Rethinking the problem a bit, you see that what you’d really like to do is create a framework that ties the store and the pizza creation together, yet still allows things to remain flexible.
In our early code, before the SimplePizzaFactory, we had the pizza-making code tied to the PizzaStore, but it wasn’t flexible. So, how can we have our pizza and eat it too?
A framework for the pizza store
There is a way to localize all the pizza-making activities to the PizzaStore class, and to give the franchises freedom to have their own regional style.
What we’re going to do is put the createPizza() method back into PizzaStore, but this time as an abstract method, and then create a PizzaStore subclass for each regional style.
First, let’s look at the changes to the PizzaStore:
Now we’ve got a store waiting for subclasses; we’re going to have a subclass for each regional type (NYPizzaStore, ChicagoPizzaStore, CaliforniaPizzaStore) and each subclass is going to make the decision about what makes up a pizza. Let’s take a look at how this is going to work.
Allowing the subclasses to decide
Remember, the Pizza Store already has a well-honed order system in the orderPizza() method and you want to ensure that it’s consistent across all franchises.
What varies among the regional Pizza Stores is the style of pizzas they make—New York pizza has thin crust, Chicago pizza has thick, and so on—and we are going to push all these variations into the createPizza() method and make it responsible for creating the right kind of pizza. The way we do this is by letting each subclass of Pizza Store define what the createPizza() method looks like. So, we’ll have a number of concrete subclasses of Pizza Store, each with its own pizza variations, all fitting within the Pizza Store framework and still making use of the well-tuned orderPizza() method.
Well, think about it from the point of view of the PizzaStore’s orderPizza() method: it is defined in the abstract PizzaStore, but concrete types are only created in the subclasses.
Now, to take this a little further, the orderPizza() method does a lot of things with a Pizza object (like prepare, bake, cut, box), but because Pizza is abstract, orderPizza() has no idea what real concrete classes are involved. In other words, it’s decoupled!
When orderPizza() calls createPizza(), one of your subclasses will be called into action to create a pizza. Which kind of pizza will be made? Well, that’s decided by the choice of pizza store you order from, NYStylePizzaStore or ChicagoStylePizzaStore.
So, is there a real-time decision that subclasses make? No, but from the perspective of orderPizza(), if you chose a NYStylePizzaStore, that subclass gets to determine which pizza is made. So the subclasses aren’t really “deciding”—it was you who decided by choosing which store you wanted—but they do determine which kind of pizza gets made.
Let’s make a Pizza Store
Being a franchise has its benefits. You get all the PizzaStore functionality for free. All the regional stores need to do is subclass PizzaStore and supply a createPizza() method that implements their style of pizza. We’ll take care of the big three pizza styles for the franchisees.
Here’s the New York regional style:
Note
* Note that the orderPizza() method in the superclass has no clue which Pizza we are creating; it just knows it can prepare, bake, cut, and box it!
Once we’ve got our PizzaStore subclasses built, it will be time to see about ordering up a pizza or two. But before we do that, why don’t you take a crack at building the Chicago-style and California-style pizza stores on the next page?
Declaring a factory method
With just a couple of transformations to the PizzaStore class, we’ve gone from having an object handle the instantiation of our concrete classes to a set of subclasses that are now taking on that responsibility. Let’s take a closer look:
Let’s see how it works: ordering pizzas with the pizza factory method
So how do they order?
First, Joel and Ethan need an instance of a PizzaStore. Joel needs to instantiate a ChicagoPizzaStore and Ethan needs a NYPizzaStore.
With a PizzaStore in hand, both Ethan and Joel call the orderPizza() method and pass in the type of pizza they want (cheese, veggie, and so on).
To create the pizzas, the createPizza() method is called, which is defined in the two subclasses NYPizzaStore and ChicagoPizzaStore. As we defined them, the NYPizzaStore instantiates a NY-style pizza, and the ChicagoPizzaStore instantiates a Chicago-style pizza. In either case, the Pizza is returned to the orderPizza() method.
The orderPizza() method has no idea what kind of pizza was created, but it knows it is a pizza and it prepares, bakes, cuts, and boxes it for Ethan and Joel.
Let’s check out how these pizzas are really made to order...
We’re just missing one thing: Pizzas!
Our Pizza Store isn’t going to be very popular without some pizzas, so let’s implement them
Note
REMEMBER: we don’t provide import and package statements in the code listings. Get the complete source code from the wickedlysmart website at https://wickedlysmart.com/head-first-design-patterns
If you lose this URL, you can always quickly find it in the Intro section.
Now we just need some concrete subclasses...how about defining New York and Chicago-style cheese pizzas?
You’ve waited long enough. Time for some pizzas!
It’s finally time to meet the Factory Method Pattern
All factory patterns encapsulate object creation. The Factory Method Pattern encapsulates object creation by letting subclasses decide what objects to create. Let’s check out these class diagrams to see who the players are in this pattern:
The Creator classes
The Product classes
View Creators and Products in Parallel
For every concrete Creator, there’s typically a whole set of products that it creates. Chicago pizza creators create different types of Chicago-style pizza, New York pizza creators create different types of New York—style pizza, and so on. In fact, we can view our sets of Creator classes and their corresponding Product classes as parallel hierarchies.
Let’s look at the two parallel class hierarchies and see how they relate:
Note
The factory method is the key to encapsulating this knowledge.
Design Puzzle
We need another kind of pizza for those crazy Californians (crazy in a good way, of course). Draw another parallel set of classes that you’d need to add a new California region to our PizzaStore.
Okay, now write the five most bizarre things you can think of to put on a pizza. Then, you’ll be ready to go into business making pizza in California!
_____________________________________________________________
_____________________________________________________________
_____________________________________________________________
_____________________________________________________________
_____________________________________________________________
Factory Method Pattern defined
It’s time to roll out the official definition of the Factory Method Pattern:
Note
The Factory Method Pattern defines an interface for creating an object, but lets subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
As with every factory, the Factory Method Pattern gives us a way to encapsulate the instantiations of concrete types. Looking at the class diagram below, you can see that the abstract Creator class gives you an interface with a method for creating objects, also known as the “factory method.” Any other methods implemented in the abstract Creator are written to operate on products produced by the factory method. Only subclasses actually implement the factory method and create products.
As in the official definition, you’ll often hear developers say, “the Factory Method pattern lets subclasses decide which class to instantiate.” Because the Creator class is written without knowledge of the actual products that will be created, we say “decide” not because the pattern allows subclasses themselves to decide, but rather, because the decision actually comes down to which subclass is used to create the product.
Note
You could ask them what “decides” means, but we bet you now understand this better than they do!
there are no Dumb Questions
Q: What’s the advantage of the Factory Method Pattern when you only have one ConcreteCreator?
A: The Factory Method Pattern is useful if you’ve only got one concrete creator because you are decoupling the implementation of the product from its use. If you add additional products or change a product’s implementation, it will not affect your Creator (because the Creator is not tightly coupled to any ConcreteProduct).
Q: Would it be correct to say that our NY and Chicago stores are implemented using Simple Factory? They look just like it.
A: They’re similar, but used in different ways. Even though the implementation of each concrete store looks a lot like the SimplePizzaFactory, remember that the concrete stores are extending a class that has defined createPizza() as an abstract method. It is up to each store to define the behavior of the createPizza() method. In Simple Factory, the factory is another object that is composed with the PizzaStore.
Q: Are the factory method and the Creator class always abstract?
A: No, you can define a default factory method to produce some concrete product. Then you always have a means of creating products even if there are no subclasses of the Creator class.
Q: Each store can make four different kinds of pizzas based on the type passed in. Do all concrete creators make multiple products, or do they sometimes just make one?
A: We implemented what is known as the parameterized factory method. It can make more than one object based on a parameter passed in, as you noticed. Often, however, a factory just produces one object and is not parameterized. Both are valid forms of the pattern.
Q: Your parameterized types don’t seem “type-safe.” I’m just passing in a String! What if I asked for a “CalmPizza”?
A: You are certainly correct, and that would cause what we call in the business a “runtime error.” There are several other more sophisticated techniques that can be used to make parameters more “type safe”—in other words, to ensure errors in parameters can be caught at compile time. For instance, you can create objects that represent the parameter types, use static constants, or use enums.
Q: I’m still a bit confused about the difference between Simple Factory and Factory Method. They look very similar, except that in Factory Method, the class that returns the pizza is a subclass. Can you explain?
A: You’re right that the subclasses do look a lot like Simple Factory; however, think of Simple Factory as a one-shot deal, while with Factory Method you are creating a framework that lets the subclasses decide which implementation will be used. For example, the orderPizza() method in the Factory Method Pattern provides a general framework for creating pizzas that relies on a factory method to actually create the concrete classes that go into making a pizza. By subclassing the PizzaStore class, you decide what concrete products go into making the pizza that orderPizza() returns. Compare that with Simple Factory, which gives you a way to encapsulate object creation, but doesn’t give you the flexibility of Factory Method because there is no way to vary the products you’re creating.
Guru: Tell me about your training.
Student: Guru, I have taken my study of “encapsulate what varies” further.
Guru: Go on...
Student: I have learned that one can encapsulate the code that creates objects. When you have code that instantiates concrete classes, this is an area of frequent change. I’ve learned a technique called “factories” that allows you to encapsulate this behavior of instantiation.
Guru: And these “factories,” of what benefit are they?
Student: There are many. By placing all my creation code in one object or method, I avoid duplication in my code and provide one place to perform maintenance. That also means clients depend only upon interfaces rather than the concrete classes required to instantiate objects. As I have learned in my studies, this allows me to program to an interface, not an implementation, and that makes my code more flexible and extensible in the future.
Guru: Yes, your OO instincts are growing. Do you have any questions for your guru today?
Student: Guru, I know that by encapsulating object creation I am coding to abstractions and decoupling my client code from actual implementations. But my factory code must still use concrete classes to instantiate real objects. Am I not pulling the wool over my own eyes?
Guru: Object creation is a reality of life; we must create objects or we will never create a single Java application. But, with knowledge of this reality, we can design our code so that we have corralled this creation code like the sheep whose wool you would pull over your eyes. Once corralled, we can protect and care for the creation code. If we let our creation code run wild, then we will never collect its “wool.”
Student: Guru, I see the truth in this.
Guru: As I knew you would. Now, please go and meditate on object dependencies.
Looking at object dependencies
When you directly instantiate an object, you are depending on its concrete class. Take a look at our very dependent PizzaStore one page back. It creates all the pizza objects right in the PizzaStore class instead of delegating to a factory.
If we draw a diagram representing that version of the PizzaStore and all the objects it depends on, here’s what it looks like:
The Dependency Inversion Principle
It should be pretty clear that reducing dependencies to concrete classes in our code is a “good thing.” In fact, we’ve got an OO design principle that formalizes this notion; it even has a big, formal name: Dependency Inversion Principle.
Note
Yet another phrase you can use to impress the execs in the room! Your raise will more than offset the cost of this book, and you’ll gain the admiration of your fellow developers.
Here’s the general principle:
At first, this principle sounds a lot like “Program to an interface, not an implementation,” right? It is similar; however, the Dependency Inversion Principle makes an even stronger statement about abstraction. It suggests that our high-level components should not depend on our low-level components; rather, they should both depend on abstractions.
Note
A “high-level” component is a class with behavior defined in terms of other, “low-level” components.
For example, PizzaStore is a high-level component because its behavior is defined in terms of pizzas — it creates all the different pizza objects, and prepares, bakes, cuts, and boxes them, while the pizzas it uses are low-level components.
But what the heck does that mean?
Well, let’s start by looking again at the pizza store diagram on the previous page. PizzaStore is our “high-level component” and the pizza implementations are our “low-level components,” and clearly PizzaStore is dependent on the concrete pizza classes.
Now, this principle tells us we should instead write our code so that we are depending on abstractions, not concrete classes. That goes for both our high-level modules and our low-level modules.
But how do we do this? Let’s think about how we’d apply this principle to our very dependent PizzaStore implementation...
Applying the Principle
Now, the main problem with the very dependent PizzaStore is that it depends on every type of pizza because it actually instantiates concrete types in its orderPizza() method.
While we’ve created an abstraction, Pizza, we’re nevertheless creating concrete Pizzas in this code, so we don’t get a lot of leverage out of this abstraction.
How can we get those instantiations out of the orderPizza() method? Well, as we know, the Factory Method Pattern allows us to do just that.
So, after we’ve applied the Factory Method Pattern, our diagram looks like this:
After applying Factory Method, you’ll notice that our high-level component, the PizzaStore, and our low-level components, the pizzas, both depend on Pizza, the abstraction. Factory Method is not the only technique for adhering to the Dependency Inversion Principle, but it is one of the more powerful ones.
Where’s the “inversion” in Dependency Inversion Principle?
The “inversion” in the name Dependency Inversion Principle is there because it inverts the way you typically might think about your OO design. Look at the diagram on the previous page. Notice that the low-level components now depend on a higher-level abstraction. Likewise, the high-level component is also tied to the same abstraction. So, the top-to-bottom dependency chart we drew a couple of pages back has inverted itself, with both high-level and low-level modules now depending on the abstraction.
Let’s also walk through the thinking behind the typical design process and see how introducing the principle can invert the way we think about the design...
Inverting your thinking...
Okay, so you need to implement a Pizza Store. What’s the first thought that pops into your head?
Right, you start at the top and follow things down to the concrete classes. But, as you’ve seen, you don’t want your pizza store to know about the concrete pizza types, because then it’ll be dependent on all those concrete classes!
Now, let’s “invert” your thinking...instead of starting at the top, start at the Pizzas and think about what you can abstract.
Right! You are thinking about the abstraction Pizza. So now, go back and think about the design of the Pizza Store again.
Close. But to do that you’ll have to rely on a factory to get those concrete classes out of your Pizza Store. Once you’ve done that, your different concrete pizza types depend only on an abstraction, and so does your store. We’ve taken a design where the store depended on concrete classes and inverted those dependencies (along with your thinking).
A few guidelines to help you follow the Principle...
The following guidelines can help you avoid OO designs that violate the Dependency Inversion Principle:
No variable should hold a reference to a concrete class.
Note
If you use new, you’ll be holding a reference to a concrete class. Use a factory to get around that!
No class should derive from a concrete class.
Note
If you derive from a concrete class, you’re depending on a concrete class. Derive from an abstraction, like an interface or an abstract class.
No method should override an implemented method of any of its base classes.
Note
But wait, aren’t these guidelines impossible to follow? If I follow these, I’ll never be able to write a single program!
You’re exactly right! Like many of our principles, this is a guideline you should strive for, rather than a rule you should follow all the time. Clearly, every single Java program ever written violates these guidelines!
But, if you internalize these guidelines and have them in the back of your mind when you design, you’ll know when you are violating the principle and you’ll have a good reason for doing so. For instance, if you have a class that isn’t likely to change, and you know it, then it’s not the end of the world if you instantiate a concrete class in your code. Think about it; we instantiate String objects all the time without thinking twice. Does that violate the principle? Yes. Is that okay? Yes. Why? Because String is very unlikely to change.
If, on the other hand, a class you write is likely to change, you have some good techniques like Factory Method to encapsulate that change.
Meanwhile, back at the Pizza Store...
The design for the Pizza Store is really shaping up: it’s got a flexible framework and it does a good job of adhering to design principles.
Now, the key to Objectville Pizza’s success has always been fresh, quality ingredients, and what you’ve discovered is that with the new framework your franchises have been following your procedures, but a few franchises have been substituting inferior ingredients in their pizzas to lower costs and increase their margins. You know you’ve got to do something, because in the long term this is going to hurt the Objectville brand!
Note
That is, the baking, the cutting, the boxing, and so on...
Ensuring consistency in your ingredients
So how are you going to ensure each franchise is using quality ingredients? You’re going to build a factory that produces them and ships them to your franchises!
Now there’s only one problem with this plan: the franchises are located in different regions and what is red sauce in New York is not red sauce in Chicago. So, you have one set of ingredients that needs to be shipped to New York and a different set that needs to be shipped to Chicago. Let’s take a closer look:
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? Austin?
For this to work, you’re going to have to figure out how to handle families of ingredients.
Note
In total, these three regions make up ingredient families, with each region implementing a complete family of ingredients.
Building the ingredient factories
Now we’re going to build a factory to create our ingredients; the factory will be responsible for creating each ingredient in the ingredient family. In other words, the factory will need to create dough, sauce, cheese, and so on... You’ll see how we are going to handle the regional differences shortly.
Let’s start by defining an interface for the factory that is going to create all our ingredients:
With that interface, 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
Okay, here’s the implementation for the New York ingredient factory. This factory specializes in Marinara Sauce, Reggiano Cheese, Fresh Clams, etc.
Reworking the pizzas, continued...
Now that you’ve got an abstract Pizza class to work from, it’s time to create the New York— and Chicago-style Pizzas—only this time around, they’ll get their ingredients straight from the factory. The franchisees’ days of skimping on ingredients are over!
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.
Here’s the CheesePizza:
Let’s check out the ClamPizza as well:
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).
An Abstract Factory provides an interface for a family of products. What’s a family? In our case, it’s all the things we need to make a pizza: dough, sauce, cheese, meats, and veggies.
From the abstract factory, we derive one or more concrete factories that produce the same products, but with different implementations.
We then write our code so that it uses the factory to create products. By passing in a variety of factories, we get a variety of implementations of those products. But our client code stays the same.
More pizza for Ethan and Joel...
From here things change, because we are using an ingredient factory
Abstract Factory Pattern defined
We’re adding yet another factory pattern to our pattern family, one that lets us create families of products. Let’s check out the official definition for this pattern:
Note
The Abstract Factory Pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes.
We’ve certainly seen that Abstract Factory allows a client to use an abstract interface to create a set of related products without knowing (or caring) about the concrete products that are actually produced. In this way, the client is decoupled from any of the specifics of the concrete products. Let’s look at the class diagram to see how this all holds together:
That’s a fairly complicated class diagram; let’s look at it all in terms of our PizzaStore:
Is that a factory method lurking inside the Abstract Factory?
Good catch! Yes, often the methods of an Abstract Factory are implemented as factory methods. It makes sense, right? The job of an Abstract Factory is to define an interface for creating a set of products. Each method in that interface is responsible for creating a concrete product, and we implement a subclass of the Abstract Factory to supply those implementations. So, factory methods are a natural way to implement your product methods in your abstract factories.
Factory Method and Abstract Factory compared
Note
The product subclasses create parallel sets of product families. Here we have a New York ingredient family and a Chicago family.
Tools for your Design Toolbox
In this chapter, we added two more tools to your toolbox: Factory Method and Abstract Factory. Both patterns encapsulate object creation and allow you to decouple your code from concrete types.
Design Patterns Crossword
It’s been a long chapter. Grab a slice of Pizza and relax while doing this crossword; all of the solution words are from this chapter.
ACROSS
1. In Factory Method, each franchise is a ________.
4. In Factory Method, who decides which class to instantiate?
6. Role of PizzaStore in the Factory Method Pattern.
7. All New York—style pizzas use this kind of cheese.
8. In Abstract Factory, each ingredient factory is a _______.
9. When you use new, you are programming to an ___________.
11. createPizza() is a ____________.
12. Joel likes this kind of pizza.
13. In Factory Method, the PizzaStore and the concrete Pizzas all depend on this abstraction.
14. When a class instantiates an object from a concrete class, it’s ___________ on that object.
15. All factory patterns allow us to __________ object creation.
DOWN
2. We used ___________ in Simple Factory and Abstract Factory, and inheritance in Factory Method.
3. Abstract Factory creates a ___________ of products.
5. Not a REAL factory pattern, but handy nonetheless.
10. Ethan likes this kind of pizza.
Design Puzzle Solution
We need another kind of pizza for those crazy Californians (crazy in a good way, of course). Draw another parallel set of classes that you’d need to add a new California region to our PizzaStore.
Okay, now write the five silliest things you can think of to put on a pizza. Then, you’ll be ready to go into business making pizza in California!
Design Patterns Crossword Solution
It’s been a long chapter. Grab a slice of Pizza and relax while doing this crossword; all of the solution words are from this chapter. Here’s the solution.
Get Head First Design Patterns, 2nd Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.