This one talks about the power of Interfaces, and how polymorphism can make things highly flexible and decoupled. Interface is a simple view of your class-to-be to the outside world, a perfect form of abstraction. Wikipedia says
A protocol or interface is a common means for unrelated objects to communicate with each other. These are definitions of methods and values which the objects agree upon in order to cooperate.
Lets take an example for better understanding. Assume, I am creating my good old calculator again.
public class Calculator{
public Button[] Buttons;
public DisplayPanel UserDisplay;
private int _operand1;
private int _operand2;
private char _operator;
private int calculate(){
}
}
Being poor, I can’t afford any staff to work on my jazzy calculator. I have come up with all my classes and stuff, but can’t start working on my calculators logic, as my Calculator
has an object of type DisplayPanel
, and I will have to complete the implementation of this class
in order to use it in Calculator
otherwise my compiler will simply keep complaining and won’t let me proceed. But Calculator
seems far more interesting to start with than DisplayPanel
. So what do I do? You may say, I will write the DisplayPanel
class
and leave all the functions blank!! Smart 🙂 What about the non-void
functions if any? You would return some random value based on the type of the return type of the function. Well, why do all this? I would simply go ahead and replace the type of UserDisplay
from DisplayPanel
with its interface (say IDisplayPanel
).
public interface IDisplayPanel{
void Show(string displayString);
}
public class Calculator{
public IDisplayPanel UserDisplay;
//...
}
Now, I can bindass go ahead and keep calling UserDisplay.Show()
, without the trouble of keeping my compiler happy. This code will build with no compile errors, but will fail at run time since you can only call a function on a solid class instance, not interfaces. So how do I know if what I have written so far works or not? Well unit tests with mocking is the answer, I would like to defer this topic for a post in the near future.
After showing my Calculator
idea to some biggy investors in the silicon valley, I manage to get funding to hire 2 assistants. Yippee, now I can get some work on the DisplayPanel
started with their help. I assign this task to Bluto, one of my assistants. While my other assistant, Popeye, has no work for the time being and is enjoying spinach with Olive 🙂 Brutus thinks he has come up with an exciting implementation and even I have finished working on my Calculator
Time to merge the code gentlemen!
Scary??? No, all that we need to do is assign the UserDisplay
to a concrete class, the one Brutus wrote, before I make any call to any of its methods. Best place is in the constructor.
public class DisplayPanelByBrutus:IDisplayPanel{
void Show(string displayString){
//...
}
}
public class Calculator{
public IDisplayPanel UserDisplay;
public Calculator(){
UserDisplay=new DisplayPanelByBrutus();
//...
}
//...
}
Great !!! All works fine, but….. Brutus, as dumb as he is, came up with a very lame and irritating implementation of the IDisplayPanel
, this one throws a big message box (IE Style) to the user with the content to display, that too every time I press any button on the calculator :'( !! Popeye, meanwhile knew how pissed I would be, and had already prepared another implementation that he was ready to show me. Better than I thought, a sleek panel on top of all my calculator buttons to immediately show what key the user presses and the result too. Yes, the windows calculator is a complete rip off of Popeye’s version. Wow!!! Totally wanted to get this into my production code. But code change again!!! Well this time it’s just a one line change, in fact half a line.
public class BakwasDisplayPanelByBrutus:IDisplayPanel{
void Show(string displayString){
//...
}
}
public class AwesomeDisplayPanelByPopeye:IDisplayPanel{
void Show(string displayString){
//...
}
}
public class Calculator{
public IDisplayPanel UserDisplay;
public Calculator(){
//UserDisplay=new BakwasDisplayPanelByBrutus();
UserDisplay=new AwesomeDisplayPanelByPopeye();
//...
}
//...
}
Hope the IMagic
got through to you by now. Interfaces allow us to decouple our implementations. You are not bound to someone’s speed of coding. Before you get working on your code that uses someone’s code all you need to do now is just sit together, analyze and agree upon an interface that you both are comfortable with, and both can proceed peacefully and independently. Also, this allows easy migration from one implementation to the other, imagine if you had to change the implementation in case of concrete classes, you would change the body of all your functions, and if the new implementation turned out to be worse, you don’t even have the old code to rollback to, unless you have taken some backup, which again would be tedious to some extent. Here all you do is just change the constructor, and still both the implementations co-exist, this gives the user a choice as well.
Another, in my opinion more powerful, advantage that interfaces give, is when we are actually dealing with enterprise software that depend on third party pluggable code. (You may want to skip to the end, if you are an absolute beginner). In a more complex aka real-world application, you may not have all the code in the same file, in fact not even in same project. Here all you would have is the interface, which would generally be shared across projects. In such cases, your Calculator
class won’t even know there is something called AwesomeDisplayPanelByPopeye
, only the classes using this calculator may know about it. Hence your statement
UserDisplay=new AwesomeDisplayPanelByPopeye();
Will give a compile error as the class AwesomeDisplayPanelByPopeye
is not present in any of the referenced libraries. In such cases, or even otherwise, a concept named Dependency Injection comes into play. In our case, the Calculator
is dependent on IDisplayPanel
, we don’t want the calculator to decide which implementation of this interface to use, but let the user of the Calculator
decide it instead. This is done in various ways, one of them is through the constructor. We remove the default parameter-less constructor, and instead use a constructor that takes an object of type IDisplayPanel
, and assign this object to our UserPanel
as follows.
public class Calculator{
public IDisplayPanel UserDisplay;
public Calculator(IDisplayPanel someDisplayPanel){
UserDisplay = someDisplayPanel;
//...
}
//...
}
Now, your code is truly agnostic of the implementation of IDisplayPanel that it will use. You can now have n different Calculators from the single Calculator
that you wrote. Today’s world is all about choices, more choice you give, more people will use it 🙂
Calculator PopeyesCalculator = new Calculator(new AwesomeDisplayPanelByPopeye());
Calculator BlutosCalculator = new Calculator(new BakwasDisplayPanelByBrutus());
Calculator YourCalculator = new Calculator(new AwesomestDisplayPanelByYou());
public class AwesomestDisplayPanelByYou:IDisplayPanel{
void Show(string displayString){
//...
}
}
There are many more benefits of interfaces than what I tried demonstrating above. Hoping this helps you in writing better, reusable, efficient and beautiful code =D