Factory Design Pattern

A Factory produces a family of products for us
so that we can easily order them for our use just by its tagged name .

The Factory is one of the most widely used creational design pattern. It hides the creation of a family of products, so that we can dynamically choose them for our usage just using a mapped name or a simple set of identifiers.

Benefits of using Factory Pattern

First of all, by hiding the creational logic, it simplifies the dependency for its clients. In other words, the clients can simply depend on a base class to use a long list of its sub-classes.

Secondly, a factory also helps us generalize our usage logic. Thus, making our design open for extension, but closed for modification.

Moreover, in case of invariant(client/context independent) instances, a factory can cache & share a single instance. Hence, a factory can also optimize the instance lifecycle and make our application light weight. We will discuss more on this in the Flyweight design pattern.

We will look at all these points in more detail with diagrams and code snippets in the sections below. Let’s start with exploring how the factory works.

 

How does the pattern work?

Let’s say we are developing a loan processing module where each loan type has a different processor. The below diagram shows how can we use a factory to simplify dependencies and generalize the processing logic.

The key point to note in the above diagram are :

1. Deferred Instantiation for Decoupling the Products

Here we have a family of loan processors but, the client only needs to know their base type i.e. ILoanProcessor.

By deferring the instantiation, the factory allows us to dynamically choose a product to replace our base product. Moreover, the factory only needs a simple identifier to decide on the actual product.

Thus, a factory greatly reduces the dependencies for its clients. In fact, it shifts the dependencies on a family of products from the clients to itself.

As regards the identities, we have mapped processors one to one with their loan types. In case the products are complex, we may need a multiple set of parameters to identify and create their instances.

public class LoanProcessorFactory {
        //Other code...
	private static Map<LoanType, LoanProcessor> registry;
	
	static {
		registry = new HashMap<LoanType, LoanProcessor>();
		registry.put(LoanType.NEW_HOME, new HomeLoanProcessor());
		registry.put(LoanType.HOME_RENOVATION, new HomeRenovationLoanProcessor());
		registry.put(LoanType.HOME_CONSTRUCTION, new HomeConstructionLoanProcessor());
		//For any new loan processor - Add them to this registry !
	}
	
	public LoanProcessor getLoanProcessor(LoanType loanType) {//throws Exception{		
		return registry.get(loanType);		
	}
        //Other code...	
}

2. Factory Helps us Generalize our Code

The two loan processing methods shows the difference in processing logic with and without using a factory. It clearly shows how a factory makes our processing logic generic and flexible.

For instance, for a new loan type we simply have to register the instance of the new processor in the factory. There is no need to modify the processing logic inside the clients.

public class WithAndWithoutFactoryDemo {
	//Other code ........
	
    public void processLoanWithoutFactory(LoanApplication loanApplication) {
    	System.out.println("\n Start : processLoanWithoutFactory.....");
    	if(LoanType.NEW_HOME == loanApplication.getLoanType()) {
    		new HomeLoanProcessor().process(loanApplication);
    	} else if(LoanType.HOME_RENOVATION == loanApplication.getLoanType()) {
    		new HomeRenovationLoanProcessor().process(loanApplication);
    	} else if(LoanType.HOME_CONSTRUCTION == loanApplication.getLoanType()) {
    		new HomeRenovationLoanProcessor().process(loanApplication);
    	}
    	//Keep adding the conditional blocks for each new loan here........
    }
    
    public void processLoanWithFactory(LoanApplication loanApplication) {
    	System.out.println("\n Start : processLoanWithFactory.....");
    	//The client code is dependent only on base loan processor...
    	LoanProcessor loanProcessor = LoanProcessorFactory.getInstance()
    								  .getLoanProcessor(loanApplication.getLoanType());
    	
    	//The logic is generic - Simply register the new processor instance in the factory! 
    	loanProcessor.process(loanApplication);
    }
}
3. Factory can Optimize the Instance Management

Since a factory centrally controls the instantiation process, it allows us to instantiate once and then re-use the instances. This reusability is not only across classes but, also across the user requests.

Again, reusing the instances is absolutely fine when we are dealing with invariant(client\context independent) beans, as we have used in our example. But, just in case we need client specific instances, we can always create them on every request.

public class LoanProcessorFactory {
	private static LoanProcessorFactory instance = new LoanProcessorFactory();
	private static Map<LoanType, LoanProcessor> registry;
	
	static {//Create the instances once and save it for re-use  
		registry = new HashMap<LoanType, LoanProcessor>();
		registry.put(LoanType.NEW_HOME, new HomeLoanProcessor());
		registry.put(LoanType.HOME_RENOVATION, new HomeRenovationLoanProcessor());
		registry.put(LoanType.HOME_CONSTRUCTION, new HomeConstructionLoanProcessor());
	}
	
	//Serve the saved instances.....		
	public LoanProcessor getLoanProcessor(LoanType loanType) {//throws Exception{		
		return registry.get(loanType);		
	}
	
	public static LoanProcessorFactory getInstance() {
		return instance;
	}
}
Source Code for the Demo

Below are the source code for the demo example we have discussed above.

This first set shows the Factory and the client code showing its usage.

package spectutz.dp.creational.factory.loan.processor;

import java.util.HashMap;
import java.util.Map;

import spectutz.dp.creational.factory.loan.vo.LoanType;

public class LoanProcessorFactory {
	private static LoanProcessorFactory instance = new LoanProcessorFactory();
	private static Map<LoanType, LoanProcessor> registry;
	
	static {
		registry = new HashMap<LoanType, LoanProcessor>();
		registry.put(LoanType.NEW_HOME, new HomeLoanProcessor());
		registry.put(LoanType.HOME_RENOVATION, new HomeRenovationLoanProcessor());
		registry.put(LoanType.HOME_CONSTRUCTION, new HomeConstructionLoanProcessor());
		//For any new loan processor - Add them to this registry !
	}
	
	public LoanProcessor getLoanProcessor(LoanType loanType) {//throws Exception{		
		return registry.get(loanType);		
	}
	
	public static LoanProcessorFactory getInstance() {
		return instance;
	}
}
package spectutz.dp.creational.factory.loan;

import spectutz.dp.creational.factory.loan.processor.HomeLoanProcessor;
import spectutz.dp.creational.factory.loan.processor.HomeRenovationLoanProcessor;
import spectutz.dp.creational.factory.loan.processor.LoanProcessor;
import spectutz.dp.creational.factory.loan.processor.LoanProcessorFactory;
import spectutz.dp.creational.factory.loan.vo.LoanApplication;
import spectutz.dp.creational.factory.loan.vo.LoanType;

public class WithAndWithoutFactoryDemo {
	
	public static void main(String[] args) {
		LoanApplication loanApplication = new LoanApplication();
		loanApplication.setLoanType(LoanType.HOME_RENOVATION);
		
		WithAndWithoutFactoryDemo demoClient = new WithAndWithoutFactoryDemo();
		demoClient.processLoanWithoutFactory(loanApplication);
		demoClient.processLoanWithFactory(loanApplication);
		
	}
	
    public void processLoanWithoutFactory(LoanApplication loanApplication) {
    	System.out.println("\n Start : processLoanWithoutFactory.....");
    	if(LoanType.NEW_HOME == loanApplication.getLoanType()) {
    		new HomeLoanProcessor().process(loanApplication);
    	} else if(LoanType.HOME_RENOVATION == loanApplication.getLoanType()) {
    		new HomeRenovationLoanProcessor().process(loanApplication);
    	} else if(LoanType.HOME_CONSTRUCTION == loanApplication.getLoanType()) {
    		new HomeRenovationLoanProcessor().process(loanApplication);
    	}
    	//Add the logic for new loans here........
    }
    
    public void processLoanWithFactory(LoanApplication loanApplication) {
    	System.out.println("\n Start : processLoanWithFactory.....");
    	LoanProcessor loanProcessor = LoanProcessorFactory.getInstance()
    								  .getLoanProcessor(loanApplication.getLoanType());
    	
    	//The logic is generic - Simply add the new processor in the factory! 
    	loanProcessor.process(loanApplication);
    }

}

This second set includes the product family of loan processors and the loan object.

package spectutz.dp.creational.factory.loan.processor;

import spectutz.dp.creational.factory.loan.vo.LoanApplication;

public class HomeLoanProcessor implements LoanProcessor{

	@Override
	public void process(LoanApplication loanApplication) {
		System.out.println("Process Home Loan");		
	}
	
}
package spectutz.dp.creational.factory.loan.processor;

import spectutz.dp.creational.factory.loan.vo.LoanApplication;

public class HomeRenovationLoanProcessor implements LoanProcessor{

	@Override
	public void process(LoanApplication loanApplication) {
		System.out.println("Process Home Renovation Loan");		
	}
}
package spectutz.dp.creational.factory.loan.processor;

import spectutz.dp.creational.factory.loan.vo.LoanApplication;

public class HomeConstructionLoanProcessor implements LoanProcessor{

	@Override
	public void process(LoanApplication loanApplication) {
		System.out.println("Process Home Construction Loan");		
	}
}
package spectutz.dp.creational.factory.loan.processor;

import spectutz.dp.creational.factory.loan.vo.LoanApplication;

public interface LoanProcessor {
	
	public void process(LoanApplication loanApplication);

}
package spectutz.dp.creational.factory.loan.vo;

public class LoanApplication {
	LoanType loanType;

	public LoanType getLoanType() {
		return loanType;
	}

	public void setLoanType(LoanType loanType) {
		this.loanType = loanType;
	}
}
package spectutz.dp.creational.factory.loan.vo;


public enum LoanType {
   NEW_HOME, HOME_CONSTRUCTION, HOME_RENOVATION   
}

Conclusion

To summaries, the factory is quite an useful pattern for managing the instances when we have a family of products. It helps us generalize the processing logic. Thus, allowing us to extend our deign with new specific products without affecting the core logic.

Moreover, it lets us re-use the product instances, helping us to make our application light weight.