INTRODUCTION
- Factory Method Design pattern is categorized under Creational Patterns.
- It deals with the creation of object based on the input provided by a client.
- At runtime, the object will be created.
- A factory class will be responsible for calling appropriate sub-class and creates the required instance. i.e., it contains the logic to create and return the appropriate instance based on the parameter sent to it.
- Almost all the Java application will have implemented with Factory Design Pattern, but it will be implemented based on the business requirements.
EXAMPLE
- One of the best examples of the Factory Pattern is Spring Framework’s org.springframework.beans.factory.BeanFactory class. Spring container only knows which object will be instantiated when the object is creation is requested.
WHEN TO USE?
The factory method design pattern can be used when
- Restrict the client to know about which concrete implementation will be called during runtime to complete the task. Hide object creation logic from the client.
- Make client loosely coupled with actual implementation.
- Need to define the clear separation of multiple types by creating a common interface.
- Delegate the object creation work to sub-classes so that tight coupling will be avoided.
USECASE
Consider different states in India, which are divided based on the language. Using the name of a state, get the required details.
PROBLEM
First, create an enum type (StateName.java) for different states.
public enum StateName {
TAMILNADU, KERALA;
}
|
Create a class (Tamilnadu.java) which will provide all the details about the state Tamilnadu.
public class Tamilnadu {
private String language = "tamil";
private String population = "7 crores";
private String capital = "Chennai";
public void getDetails() {
System.out.println(
"State Name: TamilNadu \nLanguage: " + language
+ "\nPopulation: " + population + "\nCapital: " + capital);
}
}
|
Create another class (Kerala.java) which will provide all the details about the state Kerala.
public class Kerala {
private String language = "malayalam";
private String population = "10 crores";
private String capital = "Trivandram";
public void getDetails() {
System.out.println(
"State Name: Kerala \nLanguage: " + language
+ "\nPopulation: " + population + "\nCapital: " + capital);
}
}
|
Now create a client (FactoryPatternClient.java) who will access the different state details by its name.
import jbr.factorypattern.common.StateName;
public class FactoryPatternClient {
public static void main(String[] args) {
StateName state = StateName.TAMILNADU;
switch (state) {
case TAMILNADU:
Tamilnadu tamilnadu = new Tamilnadu();
tamilnadu.getDetails();
break;
case KERALA:
Kerala kerala = new Kerala();
kerala.getDetails();
break;
default:
break;
}
}
}
|
Can you identify what is the problem with the above implementation?
- The Client has the knowledge of which object is getting created for different object creation.
- The Client is tightly coupled with State object.
- There is no clear separation of different State objects.
SOLUTION
We will try to solve the problem with the above implementation step by step.
Step 1
First, create an interface(State.java) which defines different behaviors of the State Object. Here, State.java can be defined with following methods.
- getAllDetails() - all the details about the state
- getLanguage() - official language of the state.
- getPopulation() - current population of the state.
- getCapital() - current capital city of the state.
public interface State {
String getAllDetails();
String getLanguage();
String getPopulation();
String getCapital();
}
|
Step 2
- Create Tamilnadu.java & Kerala.java which will implement the above State.java interface.
- Write the implementation of all the methods.
public class Tamilnadu implements State {
private String language = "tamil";
private String population = "7 crores";
private String capital = "Chennai";
@Override
public String getAllDetails() {
return "Name: TamilNadu \nLanguage: " + language
+ "\nPopulation: " + population + "\nCapital: " + capital;
}
@Override
public String getLanguage() {
return this.language;
}
@Override
public String getPopulation() {
return this.population;
}
@Override
public String getCapital() {
return this.capital;
}
}
|
public class Kerala implements State {
private String language = "malayalam";
private String population = "10 crores";
private String capital = "Trivandram";
public String getAllDetails() {
return "Name: Kerala \nLanguage: " + language
+ "\nPopulation: " + population + "\nCapital: " + capital;
}
@Override
public String getLanguage() {
return this.language;
}
@Override
public String getPopulation() {
return this.population;
}
@Override
public String getCapital() {
return this.capital;
}
}
|
Step 3
Now we will eliminate the tight coupling between the client and objection creation logic by introducing another class called Factory(StateFactory.java). The Factory will have the actual logic to call and create the object based on the input provided by a client.
import jbr.factorypattern.common.StateName;
public class StateFactory {
public State getState(StateName state) {
State result = null;
switch (state) {
case TAMILNADU:
result = new Tamilnadu();
break;
case KERALA:
result = new Kerala();
break;
default:
break;
}
return result;
}
}
|
Step 4
Now our new client (FactoryPatternClient.java) code will be lighter and loosely coupled.
import jbr.factorypattern.common.StateName;
public class FactoryPatternClient {
public static void main(String[] args) {
StateFactory stateFactory = new StateFactory();
State tamilnadu = stateFactory.getState(StateName.TAMILNADU);
System.out.println(tamilnadu.getAllDetails());
State kearla = stateFactory.getState(StateName.KERALA);
System.out.println("\n" + kearla.getAllDetails());
}
}
|
OUTPUT
Name: TamilNadu
Language: tamil
Population: 7 crores
Capital: Chennai
Name: Kerala
Language: malayalam
Population: 10 crores
Capital: Trivandram
|
Hope this example clarifies the Factory Method Design Pattern. Please share your comments and suggestions below.
Happy Knowledge Sharing!!!
No comments :
Post a Comment