Abstract Factory Design Pattern
The abstract factory pattern is one of the classic Gang of Four creational design patterns used to create families of objects, where the objects of a family are designed to work together. In the abstract factory pattern you provide an interface to create families of related or dependent objects, but you do not specify the concrete classes of the objects to create. From the client point of view, it means that a client can create a family of related objects without knowing about the object definitions and their concrete class names.
It is easy to confuse the abstract factory pattern with the factory method pattern because both design patterns deal with object creation. Both the patterns advocates the Object Oriented Programming (OOP) principle “Program to an interface, not an implementation” to abstract how the objects are created. Both design patterns help in creating client code that is loosely-coupled with object creation code, but despite the similarities, and the fact that both the patterns are often used together, they do have distinct differences.
Abstract Factory Pattern vs Factory Method Pattern
Abstract factory adds another level of abstraction to factory method. While factory method abstracts the way objects are created, abstract factory abstracts how the factories are created. The factories in turn abstracts the way objects are created. You will often hear the abstract factory design pattern referred to as a “factory of factories“.
From implementation point of view, the key difference between the factory method and abstract factory patterns is that factory method is just a method to create objects of a single type, while abstract factory is an object to create families of objects.
Another difference is that the factory method pattern uses inheritance while the abstract factory pattern uses composition. We say that that factory method uses inheritance because this pattern relies on a subclass for the required object instantiation. Recall in the Factory Method Design Pattern post where we created a createPizza()
factory method in an abstract base class and implemented the factory method in a PizzaFactory subclass for the required Pizza
object instantiation. On the other hand, the abstract factory pattern delegates responsibility to a separate object (abstract factory) dedicated to create a family of related objects. Then, through composition, the abstract factory object can be passed to the client who will use it (instead of factory method) to get the family of related objects.
Participants in the Abstract Factory Pattern
To understand how the abstract factory pattern works, let us revisit the pizza store that we developed in the Factory Method Design Pattern post. The store has seen a rapid increase in its customer base and now wants to serve their existing types of pizzas: cheese, pepperoni, and veggie in two different topping styles: Sicilian topping and Gourmet topping. Each topping style will require a different combination of products. Sicilian topping will have Goat Cheese with Tomato Sauce while Gourmet topping will have Mozzarella Cheese with California Oil Sauce. To model the new requirements of the application, we can create the concrete Cheese
products: GoatCheese
and MozzarellaCheese
and the concrete Sauce
products: TomatoSauce
and CaliforniaOilSauce
. Now, as we do not want any client to directly instantiate the products, we will abstract the way the products are created by introducing an abstract factory. We will create a BaseToppingFactory
abstract factory class and let its two concrete subclasses: SicillianToppingFactory
and GourmetToppingFactory
create our products. Here, it is important to note that an abstract factory should be designed to create families of products. So we can model SicillianToppingFactory
to create the family of MozzarellaCheese
and TomatoSauce
products and GourmetToppingFactory
to create the family of GoatCheese
and CaliforniaOilSauce
products.
Now, let us summarize the components of the abstract factory pattern in the context of the enhanced pizza store:
- AbstractProduct (
Cheese
andSauce
): Is an interface or an abstract class whose subclasses are instantiated by the abstract factory objects. - ConcreteProduct (
GoatCheese
,MozzarellaCheese
,TomatoSauce
, andCaliforniaOilSauce
): Are the concrete subclasses that implement/extend AbstractProduct. The abstract factory objects instantiate these subclasses. - AbstractFactory (
BaseToppingFactory
): Is an interface or an abstract class whose subclasses instantiate a family of AbstractProduct objects. - ConcreteFactory (
SicillianToppingFactory
andGourmetToppingFactory
): Are the concrete subclasses that implement/extend AbstractFactory. An object of this subclass instantiates a family of AbstractProduct objects. - Client: Uses AbstractFactory to get AbstractProduct objects.
Applying the Abstract Factory Pattern
To apply the abstract factory pattern in the pizza store application, let us first create the products that the factories will produce.
Cheese.java
package guru.springframework.gof.abstractFactory.topping; public interface Cheese { void prepareCheese(); }
GoatCheese.java
package guru.springframework.gof.abstractFactory.topping; public class GoatCheese implements Cheese { public GoatCheese(){ prepareCheese(); } @Override public void prepareCheese(){ System.out.println("Preparing goat cheese..."); } }
MozzarellaCheese.java
package guru.springframework.gof.abstractFactory.topping; public class MozzarellaCheese implements Cheese{ public MozzarellaCheese(){prepareCheese(); } @Override public void prepareCheese() { System.out.println("Preparing mozzarella cheese..."); } }
Sauce.java
package guru.springframework.gof.abstractFactory.topping; public interface Sauce { void prepareSauce(); }
TomatoSauce.java
package guru.springframework.gof.abstractFactory.topping; public class TomatoSauce implements Sauce { public TomatoSauce(){ prepareSauce(); } @Override public void prepareSauce() { System.out.println("Preparing tomato sauce.."); } }
CaliforniaOilSauce.java
package guru.springframework.gof.abstractFactory.topping; public class CaliforniaOilSauce implements Sauce { public CaliforniaOilSauce(){ prepareSauce(); } @Override public void prepareSauce() { System.out.println("Preparing california oil sauce.."); } }
In the above examples, we wrote the Cheese
interface, which is an AbstractProduct. Then we wrote the GoatCheese
and MozzarellaCheese
classes, which are the ConcreteProduct
to implement Cheese
. Similarly for the pizza sauce, we wrote the Sauce
interface and the TomatoSauce
and CaliforniaOilSauce
implementation classes.
Next, we will write the factories that will create the products. We will start with the abstract factory.
BaseToppingFactory.java
package guru.springframework.gof.abstractFactory; import guru.springframework.gof.abstractFactory.topping.Cheese; import guru.springframework.gof.abstractFactory.topping.Sauce; public abstract class BaseToppingFactory { public abstract Cheese createCheese(); public abstract Sauce createSauce(); }
In the example above, we wrote the BaseToppingFactory
abstract class, the abstract factory of our application. in the abstract factory, we declare the createCheese()
and createSauce()
abstract methods that return Cheese
and Product
objects respectively. As stated in the definition of abstract factory earlier, our abstract factory (BaseToppingFactory
) is providing an “interface to create families of related or dependent objects“. The related objects here are Cheese
and Sauce
, both of which are together used to create toppings. The definition also states “…but you do not specify the concrete classes of the objects to create“. As you can notice in the BaseToppingFactory
code, our abstract factory is not concerned with any of the concrete products: GoatCheese
, MozzarellaCheese
, TomatoSauce
, or CaliforniaOilSauce
. Let us now write the ConcreteFactory implementations.
SicillianToppingFactory.java
package guru.springframework.gof.abstractFactory; import guru.springframework.gof.abstractFactory.topping.Cheese; import guru.springframework.gof.abstractFactory.topping.MozzarellaCheese; import guru.springframework.gof.abstractFactory.topping.Sauce; import guru.springframework.gof.abstractFactory.topping.TomatoSauce; public class SicilianToppingFactory extends BaseToppingFactory{ @Override public Cheese createCheese(){return new MozzarellaCheese();} @Override public Sauce createSauce(){return new TomatoSauce();} }
GourmetToppingFactory.java
package guru.springframework.gof.abstractFactory; import guru.springframework.gof.abstractFactory.topping.CaliforniaOilSauce; import guru.springframework.gof.abstractFactory.topping.Cheese; import guru.springframework.gof.abstractFactory.topping.GoatCheese; import guru.springframework.gof.abstractFactory.topping.Sauce; public class GourmetToppingFactory extends BaseToppingFactory{ @Override public Cheese createCheese(){return new GoatCheese();} @Override public Sauce createSauce(){return new CaliforniaOilSauce();} }
In the above examples, we first wrote the SicillianToppingFactory
class that provides implementation of the createCheese()
method to create and return a MozzarellaCheese
object and a createSauce()
method to create and return a TomatoSauce
object. Then, we wrote the GourmetToppingFactory
class to create and return the GoatCheese
and CaliforniaOilSauce
objects. At this point let us understand the relationship of abstract factory with factory method. If you have noticed, the createCheese()
and createSauce()
are factory methods that we used in our abstract factory. In other words, we can say that an abstract factory object can use factory methods, one for each product to create. We are saying “can use” because, though this is the most common approach, it is not the only approach. Another approach is to use the Prototype pattern in an abstract factory to create products. Now that we have applied the abstract factory pattern to create the pizza topping factories, we will next update our pizza application to create pizzas using the pizza topping factories. First, let us create the pizza objects.
Pizza.java
package guru.springframework.gof.abstractFactory.product; public abstract class Pizza { public abstract void addIngredients(); public void bakePizza() { System.out.println("Pizza baked at 400 for 20 minutes."); } }
CheesePizza.java
package guru.springframework.gof.abstractFactory.product; import guru.springframework.gof.abstractFactory.BaseToppingFactory; public class CheesePizza extends Pizza { BaseToppingFactory toppingFactory; public CheesePizza(BaseToppingFactory toppingFactory){ this.toppingFactory=toppingFactory; } @Override public void addIngredients() { System.out.println("Preparing ingredients for cheese pizza."); toppingFactory.createCheese(); toppingFactory.createSauce(); } }
PepperoniPizza.java
package guru.springframework.gof.abstractFactory.product; import guru.springframework.gof.abstractFactory.BaseToppingFactory; public class PepperoniPizza extends Pizza { BaseToppingFactory toppingFactory; public PepperoniPizza(BaseToppingFactory toppingFactory) { this.toppingFactory=toppingFactory; } @Override public void addIngredients() { System.out.println("Preparing ingredients for pepperoni pizza."); toppingFactory.createCheese(); toppingFactory.createSauce(); } }
VeggiePizza.java
package guru.springframework.gof.abstractFactory.product; import guru.springframework.gof.abstractFactory.BaseToppingFactory; public class VeggiePizza extends Pizza { BaseToppingFactory toppingFactory; public VeggiePizza(BaseToppingFactory toppingFactory) { this.toppingFactory=toppingFactory; } @Override public void addIngredients() { System.out.println("Preparing ingredients for veggie pizza."); toppingFactory.createCheese(); toppingFactory.createSauce(); } }
In the examples above, we modified the CheesePizza
, PepperoniPizza
, and VeggiePizza
classes. Each pizza class is now composed of the abstract topping factory, BaseToppingFactory
. The constructor of each class will initialize a pizza object with an abstract topping factory object at run time. Also, the concrete pizza classes override the preparePizza()
method of the base Pizza
class. In the preparePizza()
method, the classes use the topping factory object (provided at run time via the constructor) to create the toppings. The concrete pizza classes are not tied to any concrete topping factory or any concrete topping product to use. This is because we have followed the basic principle to “Program to an interface, not an implementation“. Therefore, if we later introduce a new topping factory, say NeapolitanToppingFactory
to produce toppings of FontinaCheese
with ItalianPlumTomatoSauce
, we do not require changing our pizza classes to use the new variety. Now that our pizza classes are ready, we will need the factories to create them. BasePizzaFactory.java
package guru.springframework.gof.abstractFactory; import guru.springframework.gof.abstractFactory.product.Pizza; public abstract class BasePizzaFactory { public abstract Pizza createPizza(String type); }
SicilianPizzaFactory.java
package guru.springframework.gof.abstractFactory; import guru.springframework.gof.abstractFactory.product.CheesePizza; import guru.springframework.gof.abstractFactory.product.PepperoniPizza; import guru.springframework.gof.abstractFactory.product.Pizza; import guru.springframework.gof.abstractFactory.product.VeggiePizza; public class SicilianPizzaFactory extends BasePizzaFactory { @Override public Pizza createPizza(String type){ Pizza pizza; BaseToppingFactory toppingFactory= new SicilianToppingFactory(); switch (type.toLowerCase()) { case "cheese": pizza = new CheesePizza(toppingFactory); break; case "pepperoni": pizza = new PepperoniPizza(toppingFactory); break; case "veggie": pizza = new VeggiePizza(toppingFactory); break; default: throw new IllegalArgumentException("No such pizza."); } pizza.addIngredients(); pizza.bakePizza(); return pizza; } }
GourmetPizzaFactory.java
package guru.springframework.gof.abstractFactory; import guru.springframework.gof.abstractFactory.product.CheesePizza; import guru.springframework.gof.abstractFactory.product.PepperoniPizza; import guru.springframework.gof.abstractFactory.product.Pizza; import guru.springframework.gof.abstractFactory.product.VeggiePizza; public class GourmetPizzaFactory extends BasePizzaFactory { @Override public Pizza createPizza(String type){ Pizza pizza; BaseToppingFactory toppingFactory= new GourmetToppingFactory(); switch (type.toLowerCase()) { case "cheese": pizza = new CheesePizza(toppingFactory); break; case "pepperoni": pizza = new PepperoniPizza(toppingFactory); break; case "veggie": pizza = new VeggiePizza(toppingFactory); break; default: throw new IllegalArgumentException("No such pizza."); } pizza.addIngredients(); pizza.bakePizza(); return pizza; } }
In the above examples, we wrote the SicilianPizzaFactory
and GourmetPizzaFactory
subclasses of the abstract BasePizzaFactory
class. In the subclasses we wrote the code to override the createPizza()
factory method declared in BasePizzaFactory
. In the createPizza()
method, we first created the BaseToppingFactory
object that a particular pizza factory will use for topping. We then used a switch
statement to create a Pizza
object based on the parameter passed to the method. Notice that while creating a Pizza
object we initialized it by passing the BaseToppingFactory
object to the constructor. We then made calls to the addIngredients()
and bakePizza()
methods on the Pizza
object before returning it to the caller.
Now that our enhanced pizza store is ready for use, let us write a couple of unit tests to observe the abstract factory pattern at work.
GourmetPizzaFactoryTest.java
package guru.springframework.gof.abstractFactory; import guru.springframework.gof.abstractFactory.product.Pizza; import org.junit.Test; public class GourmetPizzaFactoryTest { @Test public void testCreatePizza() throws Exception { BasePizzaFactory pizzaFactory=new GourmetPizzaFactory(); Pizza cheesePizza= pizzaFactory.createPizza("cheese"); Pizza veggiePizza=pizzaFactory.createPizza("veggie"); } }
SicilianPizzaFactoryTest.java
package guru.springframework.gof.abstractFactory; import guru.springframework.gof.abstractFactory.product.Pizza; import org.junit.Test; public class SicilianPizzaFactoryTest { @Test public void testCreatePizza() throws Exception { BasePizzaFactory pizzaFactory=new SicilianPizzaFactory(); Pizza cheesePizza=pizzaFactory.createPizza("cheese"); Pizza pepperoniPizza =pizzaFactory.createPizza("pepperoni"); } }
When you run the above unit test code examples, you will see the following output:
------------------------------------------------------- T E S T S ------------------------------------------------------- Running guru.springframework.gof.abstractFactory.GourmetPizzaFactoryTest Preparing ingredients for cheese pizza. Preparing goat cheese... Preparing california oil sauce.. Pizza baked at 400 for 20 minutes. Preparing ingredients for veggie pizza. Preparing goat cheese... Preparing california oil sauce.. Pizza baked at 400 for 20 minutes. Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.46 sec - in guru.springframework.gof.abstractFactory.GourmetPizzaFactoryTest Running guru.springframework.gof.abstractFactory.SicilianPizzaFactoryTest Preparing ingredients for cheese pizza. Preparing mozzarella cheese... Preparing tomato sauce.. Pizza baked at 400 for 20 minutes. Preparing ingredients for pepperoni pizza. Preparing mozzarella cheese... Preparing tomato sauce.. Pizza baked at 400 for 20 minutes. Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.001 sec - in guru.springframework.gof.abstractFactory.SicilianPizzaFactoryTest Running guru.springframework.gof.factoryMethod.PizzaFactoryTest Preparing ingredients for cheese pizza. Pizza baked at 400 for 20 minutes. Preparing ingredients for veggie pizza. Pizza baked at 400 for 20 minutes. Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.001 sec - in guru.springframework.gof.factoryMethod.PizzaFactoryTest
Conclusion
As you get further into enterprise application development, you will you will encounter use cases for the abstract factory pattern, especially as the objects you’re creating become more complex. Its not uncommon to start off using the factory method design pattern and have your code evolve into using the abstract factory design pattern. Often you’ll find you only need one instance of the factory. If this is the case, you should consider implementing the concrete factory as a Singleton.
The source code for this post is available on github. You can download it here.
Kamil
I read very carefully your text and I think that it is possible to use another factory to create toppingFactory? Some very similar to PizzaFactory with based on parametr and return proper instance. Do you agree with me?
JM
Hello, I just finished reading this post and it’s very enlightening regarding abstract factory pattern. A question please, I see you use the pattern in the creation of TOPPINGS, and then you use another factory for creating PIZZAS. Is it OK to say you used the abstract factory for the toppings and the factory method for the pizzas? Thanks.
Also, I found another typo :). In CONCLUSION: you will you will
JM
I have understood design patterns a lot more by reading this series of posts, thanks.
Right after BaseToppingFactory.java’s declaration code, I think there’s a typo in that paragraph: it should be Cheese and Sauce, not Cheese and Product, right?
Vlad Martinkov
Why do you keep reference to BaseToppingFactory in your pizza’s classes? In fact you keep refence to your controller in model class. So, you can’t serialize model and so on.
Kiran Italiya
DO you have any solution for that problem?
Daniel Branco
Taking into account what Vlad said, it is partially true if the Pizzas were POJO’s but they seem to be business more than models; addIngredient suggest that they are building something so you should not have that kind of behavior in a model. But I believe that this was intentional since a model should be just a container of the structure of the Pizza and the Cheese/Pepperoni/Veggie pizza are in fact factories of the state of the pizza to be built.
Meenatchi Sundar
it was almost like strategy pattern u are implementing along with factory pattern. But u are not implementing abstract factory patten.