There are lots of definitions of what is called as 'Programming to Interface, not implementation". Let's try to understand this design principle.
All design principles are used to maintain variable parts in an efficient way so that the application welcomes changes in future without breaking. your code should be flexible with future enhancements or changes. This can be achieved with 'programming to interface' pattern.
Interfaces are just contracts or signatures and they don't know anything about implementations.
So let's learn it by an example.
Recently i saw a code where this design principle is not used and it lead me to write this post. Below is the similar sample of that code.
import com.sun.org.apache.xerces.internal.jaxp.datatype.XMLGregorianCalendarImpl;
import java.util.Date;
public class Utils{
public static XMLGregorianCalendarImpl getXmlGregorianCalendar(Date date) {
// create a XMLGregorianCalendarImpl instance
XMLGregorianCalendarImpl calendar= new XMLGregorianCalendarImpl();
calendar.setYear(date.getYear());
calendar.setMonth(date.getMonth());
return calendar;
}
}
In this Utils class, getXmlGregorianCalendar() is used to get the XMLGregorianCalendarImpl calender time. Since this is in a utility class, it can be used in multiple instances.
import com.sun.org.apache.xerces.internal.jaxp.datatype.XMLGregorianCalendarImpl;
import java.util.Date;
public class Usages {
public static void main(String[] args) {
Date date = new Date();
//usage 1
XMLGregorianCalendarImpl usage1 = Utils.getXmlGregorianCalendar(date);
System.out.println(usage1.getYear());
//usage 2
XMLGregorianCalendarImpl usage2 = Utils.getXmlGregorianCalendar(date);
System.out.println(usage2.getMonth());
}
}
This code was working fine in Java JDK 8 and we need to migrate code to JDK 10. Then this issue occurs. This previous XMLGregorianCalendarImpl is not included in the new version and had to use a different kind of implementation.
So we have to change the getXmlGregorianCalendar() method and all of its usages.
If it was used by many instances we need to change all of its usages and method getXmlGregorianCalendar(). Not only the body of the method but also return type needed to be changed.
Very annoying task. Isn't it? we could overcome this if we follow 'Programming to Interface' design principle. Let's redesign this code properly.
Let's look at XMLGregorianCalendarImpl class. it extends from XMLGregorianCalendar abstract class.
Let's study redesigned the Utils class.
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import java.util.Date;
import java.util.GregorianCalendar;
public class Utils{
public static XMLGregorianCalendar getXmlGregorianCalendar(Date date) {
XMLGregorianCalendar calendar = null;
GregorianCalendar c = new GregorianCalendar();
c.setTime(date);
try {
calendar = DatatypeFactory.newInstance().newXMLGregorianCalendar(c);
} catch (Exception e) {
e.printStackTrace();
}
return calendar;
}
}
Here getXmlGregorianCalendar() returns the abstract class called XMLGregorianCalendar. it does not specifically return an implementation.
Initially, if we had a method like this, then when migrating only thing we need to do is changing method body. All of its usages, method signature, and return type remains the same.
Rather than returning an implementation, returning the interface or abstract class helps to adapt future changes. So try to use this design fundamental when you are coding.
If you want to understand this principle with more simpler example please refer to this link .