Polymorphism is one of the most important concept of object oriented software design. Yet whenever I have asked questions about polymorphism during interviews, most candidates get confused while replying. The ones that do answer the basics lose it when asked about the practical cases where they have used polymorphism.
Polymorphism is the process of invoking methods on an object depending upon the type of that object as identified at runtime. Polymorphism is such a key concept that in practice it is applied in all scenarios where there is a need to generalize code so it can behave differently at runtime for specific implementations. It lets programmers code to abstractions like interfaces and abstract classes. References to interfaces can be used to invoke functions on concrete objects at runtime. This is extremely important while implementing software systems that need to have a scalable design and do not cater to one specific implementation. Some of the practical applications of polymorphism include:
- All design patterns in any object oriented language like c++, java or smalltalk. I am hard pressed to find a design pattern that can be implemented without the use of polymorphism.
- All IDEs like Weblogic Workshop, WSAD or Eclipse that discover components like page controls and generate code when those components are used in flows.
- All frameworks like struts and spring use polymorphism.
For polymorphism to work, methods that get invoked at runtime must be declared as virtual. All methods are virtual by default in java but methods have to be specifically declared as virtual in C++. Let me take an example scenario in which polymorphism can be applied followed by actual code in java.
Problem Statement: Different workflows needs to be implemented depending upon an ID passed from front end. We need to design a system that can accommodate execution of different workflows and needs to be flexible enough to scale for new workflows that are added in future
public interface IWorkflow{
public boolean executeWorkflow(InfoBean infoBean);
}
public class Workflow implements IWorkflow{
public boolean executeWorkflow(InfoBean infoBean){
//logic for executing a workflow
}
}
public class Workflow01 implements IWorkflow{
public boolean executeWorkflow(InfoBean infoBean){
//logic for executing a workflow
}
}
public class WorkflowFactory{
public static IWorkflow createWorkflow(int workflowID){
Workflow workflowInstance = null;
try{
Class workFlowClass = Class.forName("Workflow"+workflowID);
workflowInstance = workFlowClass.newInstance();
} catch (ClassNotFoundException cfe) {
workflowInstance = new Workflow(); //workflowID will be invalid if default workflow is fine
//handle exception here
} catch (InstantiationException ie) {
//handle exception
}
return workflowInstance;
}
}
public class WorkflowClient{
public void invokeWorkflow(int workflowID,InfoBean infoBean){
try{
IWorkflow flow = WorkflowFactory.createWorkflow(workflowID);
flow.executeWorflow(infoBean);
} catch (Exception ex) {
//handle exception here
}
}
In the above example, all Workflow classes derive from IWorkflow interface and implement the executeWorkflow method. WorkflowFactory returns the object of type IWorkflow by dynamically creating a concrete class of type IWorkflow. WorkflowClient class invokes the executeWorkflow() method on IWorkflow interface but the right workflow object is invoked depending upon the runtime type of workflow. The solution is extremely scalable. As and when new workflows need to be added, we create a new workflow class. There is no code change required for any other class including the WorkflowClient. This is the power of polymorphism.