Interfaces - Explained clearly once and for all

Interfaces form a very important part of the Object oriented design paradigm. They are completely abstract or hollow since they do not provide any implementation as such yet they play a critical role in loosely coupling applications by allowing design pattern implementations, helping provide services through disparate systems, unit testing and providing application extensibility and much more.  

I will explain the concept of interfaces by providing real world inspired examples, examples from .NET framework and common implementations and from my personal coding experiences. This is important as many people I've come across know about interfaces but almost all are a bit confused and never fully in control. Additionally they fail to explain where to use interfaces and their importance. 

To begin with real world examples, let's take example of an ATM machine. A bank's ATM machine in the 90s was designed to serve its customers with 'any time money' and worked wonders and soon became popular, however the more popular they got, it was deemed important and made business sense to be able to serve any bank's customer walking in to an ATM. However each bank had its own way of authentication and of providing ATM services. So we see disparate systems here and getting them to work is a challenge. It can be addressed with complex coding and logic but that will be rigid in design and inflexible to change. Using interfaces addresses the problem as interfaces define rules of contract that implementing classes need to adhere to. By providing implementation adhering to the contract guidelines, different services can be delivered through a common medium knowing nothing about implementations. We will of course not get into detail of how to make an ATM work. But using interfaces help different service providers (banks in this case) to provide services and their own implementation yet adhere to a strict contract that helps an ATM service most of its users. 

Let's take another real world example. You have a wall socket at home, office and nearly any indoor premise. That is in OOPs terms an interface at work. If you notice, any device or gadget adhering to certain rules can make use of the wall socket and get electrical supply to power itself and provide their own unique service. The interface states that any device or gadget should accept for instance 220v of current and have two/three pin plug to be able to interact with the socket and get output. Now any device be it a television, an audio player, a mobile charger, a hair dryer etc can plug and play using the socket as long as it satisfies the contract terms and provide its own implementation. 

Now let's take an example from the .NET framework. Let's take the IEnumerable interface and see what it contains. It has just one method GetEnumerator which returns and IEnumerator. In simple terms, a class implementing an IEnumerable is guaranteed to provide foreach support or support for forward only iteration. Hence it becomes very easy to work within a system where interfaces help provide guidelines and help customer and client interact without knowing about each other beforehand. You might have a method in your class accepting an IEnumerable as a parameter, in the case you can provide your implementation knowing full well that any incoming object will be a collection and provide support for iteration. 

Two more examples are a shopping cart and share option in Android. A shopping cart is a place where all kinds of products meet. Some might be delivered via post mail, some digitally over email and some might be just services that may not require conventional deliveries. Yet a shopping cart needs to process all products without knowing in advance what product it is going to get. An ebook might be delivered via email, a smartphone might be delivered in the mail at a physical address while a music subscription might require a subscription key to be sent via an sms service. Hence an interface is extremely useful in such scenarios when interaction with disparate systems is needed and knowing the type at compile time is impossible. In our shopping cart example, an interface might declare a few arbitrary members like product title, price and ship() method. Of course there are intricacies related to payment gateways etc but the essence has been conveyed in this example.  

Lastly let's take an example of sharing option in Android operating system. You can get this option for nearly any file type and any application can choose to offer support; it is at the app's developers discretion. So if for instance, you are viewing an image and hit the share option, many apps like Gmail, Whatsapp, Instagram will provide sharing functionalities. So how does this work under the hood. We will only look at this in the current context of interfaces and not the minute details of how sharing is done. So any app willing to participate in such functionality need to implement an Interface and provide implementation details of their own. So an app might first load the content on its cloud before sharing or it might compress the content. Such implementation details are at the peril of the implementing class of course but from contract and interface perspective, as long as the contract members are implemented, the class can provide its services. Hence it is worth appreciating how important a role interfaces play in bringing disparate systems under one umbrella. 

Let's now look at how interfaces help in implementation of design patterns. Many popular patterns make use of interfaces; the patterns themselves are workable workflows only by the use of interfaces as they are central to the foundation of the pattern. For instance, let's look at the factory pattern. A factory is in basic terms a class that can return an instantiated instance of an object. The factory may not know what object it is required to serve hence it returns the type encapsulated in an interface. The client is guaranteed to get functionality described in the interface. For instance let's assume we have an IConnect interface with members, void connect(), void disconnect() and IEnumerable<T> GetData(string query). The factory returns an IConnect type based on the request. The factory might be equipped to manufacture and return SQLdbOracledb or even an Excel worksheet. That will depend on what the client requests yet the client will be assured that the receiving IConnect object will support the above declared members. 

Another important role interfaces play is in the use of SOLID principles. Not really going into much detail of what these principles are, I will quickly highlight one of them and how interfaces form the core of this principle. The dependency inversion principle primarily works only because of interfaces. Interfaces are at the very core of implementation of dependency inversion by the way of dependency injection. This might be just one way of implementing this principle of course but interfaces are the types that are used for resolving . 

Most of us programmers have that creepy TestConsoleApp at all times on our computers to try out things here and there. The entry point to the application is the Main() so anytime you add a new class or any code for that matter, remember you modify the Main as well so that you can call it and see the changes.  

And then when it gets too clunky, you dump the project and setup a new one, admit you've done that at least once. Well interfaces and strategy pattern could have saved you all that hassle. Just look at it, you are always going in and modifying your code to make it run. Open/Close principle anyone? It's inflexible and resistant to change. You could however make it extensible by just using an interface. Here is how you could do it.  

Create an IProgram interface with a Run() and implement it in any new class you add code. Simple enough! Now create a RunProgram(IProgram program) in your Program class and call Run() of IProgram inside this method. In Main(), call RunProgram and provide your concrete class' object. Voila! That's it. Now extend all you want, just add one line to Main() and your new code with run flawlessly. This is the use of Strategy pattern.

The uses of interfaces are endless and the list can go on and on. Take WCF services, client knows nothing about implementation; just the interface is known. This is one of the reasons why services are heterogeneous and environment independent. Messages are in XML wrapped in SOAP envelopes and only type that matters is the interface. Even while calling a service's method, all you know is the type of interface and not the concrete type. We find them being used in unit testing, in application extensibility and scalability and the list goes on and on.  

Comments