Whenever we thought of a factory, following picture comes to our mind –
That means a huge plant where some laborers manufacture “goods” or supervise machine processing one product to another. In a generic term, a factory produces different products of similar kind (Mobira Cityman 900, Nokia N90) based on requirement in a single roof (sorry, roof might be different).
But when we are talking about Factory in object oriented design, first question come to mind is – factory of what?? Of course – Object. A Factory pattern is one that returns an instance of one of several possible classes depending on the data provided to it. In simple words, if we have a base class (e.g. B) and n derived classes (d1, d2 … dN), and based on data provided to a class, it returns any instance of the base classes, then the class is a factory.
In this figure, the factory returns d1Obj depending on the argument it receives (i.e. One). The client should not have any concern as they are using same method with different behavior. How to return is purely “Factory” decision but what to return should be based on client input.
Let us discuss the concept of Factory Design Pattern with an example. Suppose that we need to create a “Tax Calculator” for our application which needs to calculate Tax for different country.
if(lCountryName == "US") {
lTaxObj = new USTaxCalculator();
} else if (lCountryName == "UK") {
lTaxObj = new UKTaxCalculator();
} else if (lCountryName == "India") {
lTaxObj = new IndiaCalculator();
}
But this is not an efficient way as
- Lots of new keywords scattered in client code. In other words, the client is loaded with lots of object creation activities and hence the design will be complicated
- The client needs to be aware of all Class and needs to know the way to handle it
- The client code needs to change or recompile if a different kind of Tax Calculator require to support. In nutshell, the client code is tightly coupled with different “Tax Calculator”
To overcome these deficiencies, let us “establish” a Factory that “manufactures” different Tax Calculator objects. We need to introduce an Interface (so that the user know the behavior of the object return by the factory) and the Factory Class (which returns require object as per the user need)
public interface TaxCalcFactInterface {
public void CalculateTax(String pCountry);
}
And the factory class looks like
public class TaxCalcFactory {
public TaxCalcFactInterface getFactObj(String name) {
if(name.equalsIgnoreCase("US")) {
return new USTaxCalculator();
} else {
return new UKTaxCalculator();
}
}
}
The client knows only the interface and the factory. There is no connection between the client and the concrete classes (USTaxCalculator and UKTaxCalculator). So, if a new TaxCalculator needs to support, we can simply add it without changing any client code.
Below is the code snippet of how the client code looks like-
public class MainClass {
public static void main(String[] args) throws IOException {
System.out.println("Enter the country name ");
String lCountryName = new java.io.BufferedReader(new java.io.InputStreamReader(System.in)).readLine();
TaxCalcFactInterface usTaxCalc = new TaxCalcFactory().getFactObj(lCountryName);
usTaxCalc.CalculateTax();
}
}
The corresponding concrete classes can be written as
public class UKTaxCalculator implements TaxCalcFactInterface {
@Override
public void CalculateTax() {
System.out.println(" Calculatinf Tax for UK");
}
}
And
public class USTaxCalculator implements TaxCalcFactInterface {
@Override
public void CalculateTax() {
System.out.println(" Calculating Tax for United State " );
}
}
In order to avoid recompiling the client we have introduced the TaxCalcFactInterface interface. Both the concrete classes USTaxCalculator and UKTaxCalculator inherit and implement the TaxCalcFactInterface interface.
And that’s the fundamental principle of Factory patterns. This type of factory design pattern can be named as “parameterized factory” as the object creation is decided based on “parameter” passed by the client. The main disadvantage of this approach is that it is not very flexible. To add a new concrete class, the factory class needs to be modified (we are using hard coded data – if else).
Use of reflection: Instead of “hard coded” let’s use reflection to instantiate the desire object. To achieve that, we can register our class information in property object and instantiate the object without even changing the factory itself.
My property file looks like
UK=simple.factory.dp.UKTaxCalculator
US=simple.factory.dp.USTaxCalculator
And we can change our factory class as
public class TaxCalcFactoryReflect {
public static String getClassName(String pCountry) throws IOException {
Properties prop = new Properties();
FileInputStream propFile = new FileInputStream("../class.properties");
prop.load(propFile);
propFile.close();
return prop.getProperty(pCountry);
}
public TaxCalcFactInterface getFactObj(String pCountry) throws
ClassNotFoundException,
InstantiationException,
IllegalAccessException, IOException {
Class<?> clazz = Class.forName(TaxCalcFactoryReflect.
getClassName(pCountry),true, ClassLoader.getSystemClassLoader());
return (TaxCalcFactInterface) clazz.newInstance();
}
}