Background
We know design patterns are broadly categorized into three types -
- Creational
- Structural
- Behavioral
In this post we will see Factory pattern/Factory method and Abstract Factory design patterns which falls under creational design pattern.
One of the most basic OOP principle is - "Program to an interface not an implementation". This means you should not directly instantiate your object with new operator in your classes. But then that is how we create new objects in Java. Don't we? Correct! There is no escape from using new operator to instantiate object but we can surely abstract it out to decouple the object creation code.
That is the very purpose of Factory patterns to abstract out creation of Objects.
- All Factory patterns encapsulate Object creation.
Lets understand this with an example.
Simple Factory pattern
Lets say you are running a sandwich store and want to write an API to deliver sandwiches as asked by the customers.
public class SandwichStore { public Sandwich orderSandwich(String type) { Sandwich sw; if(type.equalsIgnoreCase("cheese")) { sw = new CheeseSandwich(); } else if(type.equalsIgnoreCase("grill")) { sw = new GrillSandwich(); } else { sw = new RegularSandwich(); } sw.addToppings(); sw.makeSlices(); sw.pack(); return sw; } }
where CheeseSandwich, GrillSandwich and RegularSandwich are concrete implementations of Sandwich class.
Notice code to add toppings, to slice it and packing remain same for all sandwiches. Only change is creating sandwiches depending on the order. Now if tomorrow you decide to add new types of sandwiches or remove existing ones you need to modify above code which is not correct. This is where the factory pattern comes into picture. Job of creating the sandwich is delegated to this factory. See code below -
public class SandwichFactory { public Sandwich createSandwich(String type) { Sandwich sw; if(type.equalsIgnoreCase("cheese")) { sw = new CheeseSanwich(); } else if(type.equalsIgnoreCase("grill")) { sw = new GrillSandwich(); } else { sw = new RegularSandwich(); } return sw; } }
and then our store would look like -
public class SandwichStore { SandwichFactory sf = new SandwichFactory(); public Sandwich orderSandwich(String type) { Sandwich sw = sf.createSandwich(type); sw.addToppings(); sw.makeSlices(); sw.pack(); return sw; } }
Notice how we have successfully moved the sandwich creation dependency to new object (Factory). Also SandwichStore has no need now to know about various types of (sandwiches)Object creations. This is a simple Factory pattern.
Factory Method pattern
- Factory Method pattern encapsulates object creation by letting subclass decide what object to create.
Above Sandwich store code with Factory Method will look something like -
public abstract class SandwichStore { public Sandwich orderSandwich(String type) { Sandwich sw = createSandwich(type); sw.addToppings(); sw.makeSlices(); sw.pack(); return sw; } public abstract Sandwich createSandwich(String type); } class ChineseSandwich extends SandwichStore { @Override public Sandwich createSandwich(String type) { Sandwich sw; if(type.equalsIgnoreCase("cheese")) { sw = new ChineseCheeseSanwich(); } else if(type.equalsIgnoreCase("grill")) { sw = new ChineseGrillSandwich(); } else { sw = new ChineseRegularSandwich(); } return sw; } }
Abstract Factory pattern
- Abstract Factory pattern provides an interface to create family of related objects.
- Methods of abstract factory are generally Factory methods.
public class SandwichStore { SandwichFactory sf; public SandwichStore(SandwichFactory sf) { this.sf = sf; } public Sandwich orderSandwich(String type) { Sandwich sw = sf.createSandwich(type); Sause sause = sf.createSause(); sw.addToppings(); sw.makeSlices(); sw.pack(); sw.addSause(sause); return sw; } public static void main(String args[]) { SandwichStore sw = new SandwichStore(new ChinesSandwichFactory()); sw.orderSandwich("cheese"); } } class ChinesSandwichFactory extends SandwichFactory { @Override public Sandwich createSandwich(String type) { //Return Chines type sandwiches } @Override public void createSause() { //Return Chines type sause } } class IndianChinesFactory extends SandwichFactory { @Override public Sandwich createSandwich(String type) { //Return Indian type sandwiches } @Override public void createSause() { //Return Indian type sause } }
Note : The createSandwich() and createSause() methods are in fact a Factory methods. As stated previously each method of Abstract Factory is a Factory Method.
Generally Abstract Factory class will have many such Factory methods each creating unique object of a family of products. Hence Multiple such methods create families of related products (each product corresponding to each factory method).
Note : Above code creates Family of Sandwiches and Family of sauses which are related (we add sause to the sandwich).
In above diagram each product (product1, product2 ...) corresponds to a factory method in Abstract factory (which is implemented uniquely in Factory1, Factory2...)
Difference between Factory Method and Abstract Factory
- Factory Method is a single method (perhaps abstract) that uses inheritance to delegate object creation.
- Abstract Factory is reference to abstract class whose subclass instances create actual objects. Each concrete implementation of abstract factory creates related products having same super class (interface).
- AbstractFactory pattern uses composition to delegate responsibility of creating object to another class while Factory design pattern uses inheritance and relies on derived class or sub class to create object.
No comments:
Post a Comment