Design Patterns Certification Training
- 5k Enrolled Learners
- Weekend/Weekday
- Self Paced
Welcome to the second post of Design Patterns exposed series. In this post we’ll uncover Factory Pattern. Factory pattern comes under Creational Pattern category.
What is a Creational Pattern?
Creational Patterns are concerned with object creation problems faced during software design. Many a time object creation results in design problems, creational patterns solve this problem by controlling the object creation.
Let`s understand the problem first then we will solve the problem using Factory Pattern.
Consider the below UML diagram , where we have cake abstract class and concrete sub-classes BlackForest, LitchiGateaux, BlueBerry, Pineapple.
Let’s see what goes inside these classes. Cake is the abstract class, all different cake classes inherit from this class. Cake class has three properties; name of the cake, type of the cake (whether cake contains egg or it is egg less) and the last one is price.
package co.edureka; public abstract class Cake { String name; String type; int price; public String getName(){ return name; } public String getType(){ return type; } public int getPrice(){ return price; } public void setName(String name){ this.name=name; } public void setPrice(int price){ this.price=price; } public void setType(String type){ this.type=type; } public abstract void recipe(); public abstract void myFans(); public void aboutCake(){ System.out.println("I am "+name+" Cake"); System.out.print("My Fans : "); myFans(); System.out.println("You can get a "+name+" Cake at "+price); } }
Let’s see some of the sub-classes of cake
package co.edureka; public class BlackForest extends Cake { public BlackForest(){ setName("Black Forest"); setType("Eggless"); setPrice(800); } public void recipe() { System.out.println("---BlackForest Recipe---"); System.out.println("Sieve together Maida and Cocoa powder"); System.out.println("Add Milk and Vanilla essence"); System.out.println("Top with Whipped Cream and Cherries"); System.out.println("Chill and Serve"); } public void myFans() { System.out.println("Both adults and Kids love me"); } }
package co.edureka; public class BlueBerry extends Cake { BlueBerry(){ setName("Blue Berry"); setType("Egg"); setPrice(700); } public void recipe() { System.out.println("---BlueBerry Recipe---"); System.out.println("First prepare Flour and Baking powder mixture"); System.out.println("Add Milk and Egg yolks"); System.out.println("Coat Berries"); System.out.println("Bake for 50 minutes"); } public void myFans() { System.out.println("Moms love me"); } }
package co.edureka; public class LitchiGateaux extends Cake { LitchiGateaux(){ setName("Litchi Gateaux"); setType("Eggless"); setPrice(750); } public void recipe() { System.out.println("---LitchiGateaux Recipe---"); System.out.println("Take some fresh Litchies"); System.out.println("Wash them and Grind for 5 minutes"); System.out.println("Put some groundnuts and bake for 45 minutes"); } public void myFans() { System.out.println("Litchi lovers love me"); } }
Now we are all set to create different types of cakes.
package co.edureka; import java.util.Scanner; public class CakeTest { public static void main(String args[]) { Cake cake = null; System.out.println("Which Cake you would like to eat "); Scanner scanner = new Scanner(System.in); String choice = scanner.nextLine(); scanner.close(); if (choice.equals("BlackForest")) { cake = new BlackForest(); } else if (choice.equals("BlueBerry")) { cake = new BlueBerry(); } else if (choice.equals("LitchiGateaux")) { cake = new LitchiGateaux(); } else if(choice.equals("Pineapple")){ cake=new Pineapple(); } cake.aboutCake(); } }
Notice the code in CakeTest class, we have got several concrete classes BlackForest, BlueBerry, LitchiGateaux, Pineapple being instantiated, and the decision of which class to instantiate is made at run-time depending on the user’s choice.
When you see code like the above and when the time comes for changes or extension, you will have to reopen the code and examine what needs to be added or deleted. Suppose I don’t want to offer pineapple cake, I have to reopen the code and delete some codes. Similarly, if later I decide to add 10 new cake types, I have to reopen the code and type 10 more “else if” statements. Which means your code is not “closed for modifications”.
Note : Your design should always be “open for extension” but “closed for modifications”.
Separate the code that vary
The code in the CakeTest will certainly vary as we decide to delete some cake type or add new cake types.
if (choice.equals("BlackForest")) { cake = new BlackForest(); } else if (choice.equals("BlueBerry")) { cake = new BlueBerry(); } else if (choice.equals("LitchiGateaux")) { cake = new LitchiGateaux(); } else if(choice.equals("Pineapple")){ cake=new Pineapple(); }
There are three big problems with our current design
So, anyone that needs a cake object can directly call factory class.
Now that we have understood the problem in our design, let’s separate the code that vary.
Encapsulating Object Creation
We are going to define a factory interface and a concrete factory class (CakeFactory) that implements factory interface which will encapsulate the object creation code for different types of cakes.
package co.edureka; public interface Factory { Cake createCake(String cakeName); }
package co.edureka; public class CakeFactory implements Factory{ public Cake createCake(String cakeName){ Cake cake=null; if (cakeName.equals("BlackForest")) { cake = new BlackForest(); } else if (cakeName.equals("BlueBerry")) { cake = new BlueBerry(); } else if (cakeName.equals("LitchiGateaux")) { cake = new LitchiGateaux(); } else if(cakeName.equals("Pineapple")){ cake=new Pineapple(); } return cake; } }
Now, let’s see how our CakeTest class is going to change.
package co.edureka; import java.util.Scanner; public class CakeTest { public static void main(String args[]) { Cake cake = null; System.out.println("Which Cake you would like to eat BlackForest/BlueBerry/LitchiGateaux/Pineapple : "); Scanner scanner = new Scanner(System.in); String choice = scanner.nextLine(); scanner.close(); CakeFactory cakeFactory=new CakeFactory(); cake=cakeFactory.createCake(choice); cake.aboutCake(); } }
By separating the cake creation code in a separate factory class, we get following benefits:
Consider this scenario, there is a CakeStore that gets the cake from a cake factory. Using Factory Pattern we can dynamically change the factory from where the CakeStore that gets its cakes.
So let’s do it.
package co.edureka; public class CakeStore { Factory factory; CakeStore(Factory factory){ this.factory=factory; } public Cake newCakeOrder(String cakeName,String customerName){ Cake cake; cake=factory.createCake(cakeName); cake.decorate(customerName); cake.packCake(); return cake; } }
Let’s create a cake factory that is specialized in creating Christmas cakes.
package co.edureka; public class ChristmasCakeFactory implements Factory { public Cake createCake(String cakeName){ Cake cake=null; if(cakeName.equals("Christmas Special Cake")){ cake =new ChristmasSpecialCake(); } else if(cakeName.equals("Santa Cake")){ cake =new SantaCake(); } else if(cakeName.equals("Christmas Ice Cake")){ cake =new ChristmasIceCake(); } else if(cakeName.equals("Merry Christmas Cake")){ cake =new MerryChristmasCake(); } return cake; } }
Now it’s time to test our code:
ChristmasCakeFactory christmasCakeFactory=new ChristmasCakeFactory(); CakeStore cakeStore=new CakeStore(christmasCakeFactory); Cake cake=cakeStore.newCakeOrder("Special Christmas Cake", "Alex");
Notice that in above code how we have set the ChristmasCakeFactory for the CakeStore class.
We can dynamically choose the factory from which we want to get Cakes. You can create many different factories like BirthdayCakeFactory, WeddingCakeFactory, CupCakeFactory etc.
Factory pattern is used more than a hundred times in Java API. One example is javax.swing.BorderFactory class which has many methods that return a particular border depending on the argument you pass.
AbstractBorder is an abstract class having concrete classes as LineBorder, StrokeBorder, TitledBorder, EmptyBorder, CompoundBorder, BevelBorder,EtchedBorder etc.
Now, whenever I need to get a particular type of border I will just call one of the several methods available in BorderFactory class. Say, I want to get a TitledBorder object.
There are two ways to get a TitledBorder, one way is to directly instantiate TitledBorder class and the other way is to use the BorderFactory class.
Border border=new TitledBorder("This is the Border Title");
Or
Border border=BorderFactory.createTitledBorder("This is the Border Title");
This is similar to our CakeFactory example. Say If I want to get BlackForestCake, I can do it in two ways.
Cake cake=new BlackForest();
Or
Cake cake=CakeFactory.createCake("BlackForest");
I hope you understood the Factory Pattern and when and how to use it.
As always you can download the code and play with it to make sure you really cement the Factory Pattern in your head. In the next post we’ll uncover Factory Method Pattern, which is also a type of Factory Pattern but offers more flexibility.
In case you have any query regarding Factory Pattern or any other pattern leave your comments below.
Got a question for us? Mention them in the comments section and we will get back to you.
Related Posts:
edureka.co