When trying to build maintainable, reusable, and flexible C# code, the object oriented nature of C# only gets us 50% of the way there. Programming to interfaces can provide us with the last 50%. Interfaced-based design provides loose coupling, true component-based programming, easier maintainability and it makes code reuse much more accessible because implementation is separated from the interface.
What's an Interface?
An interface is a reference type object with no implementation. You can think of it as an abstract class with all the implementation stripped out and everything that is not public removed. Abstract classes are classes that can not be instantiated. No properties or methods are actually coded in an interface, they are only defined. So the interface doesn't actually do anything but only has a signature for interaction with other classes or interfaces. A good analogy for an interface is a pencil and a pencil sharpener. When we talk of a pencil and a pencil sharpener we generally know what they do and how they interact. We don't have to know the inner workings of a particular sharpener or pencil, only how to use them through their interface.
public interface IPencil
{
void Write();
bool IsSharp { get; }
}
public interface IPencilSharpener
{
void Sharpen(IPencil pencil);
}
* While it is not enforced by the compiler, for consistency with the .NET framework it's a good idea to name interfaces starting with a capitol 'I'.
Why Use Interfaces?
This make for a much easier "code world" in which to navigate. Imagine if instead of learning how to drive a car and then being able to drive any car, we had to learn how to drive each instance of every car we get into. It would be really inefficient if after learning how to drive the Ford Pinto we had to start all over again in order to figure out the Mustang. A much more efficient way is to deal with the cars interface: the steering wheel, turn signals, gas pedal and brake. This way, no matter what is implemented on the backend of the interface, we don't really care because in the end it subscribes to the basic car contract and that is how we will deal with it (through the interface).
Following our car analogy... If we work for a company producing a component of a car, like an engine, it would be good if our engine could be used in multiple types of cars in "code world". You can see that this is crucial to the success of our business if we need to sell the engine to multiple vendors and provide them with a basic manual on how to plug it in and make it work. Our customer does not need to know anything about how we have implemented the interfaces (which is better for us and better for them - we keep our trade secrets and they don't need to hire someone with the knowledge base) so our engine is really just a "black-box". If we change the engine, we keep the contract the same (the interface) and our customers can just plug in any new upgraded engine we develop.
One of the problems we run into relying on classes where the interface is tightly coupled to their interface (which is the case when no interface is defined) is that if there is a change to the implementation there is a strong possibility that the interface will change slightly. This can create cascading breaks through inheritance chains not only in our code, but also for other projects utilizing our classes. As our projects mature and the amount of code increases, the impact of small changes becomes greater. This is where code reuse breaks down with non-interface based development.
To make our code more reusable, we can provide interfaces to our implementation. This ONLY works if we are disciplined about not altering interfaces after they have been deployed (that would cause the same cascading breaks that non-interface development does). We can always add functionality to an interface without breaking things, but if we alter the existing "hooks" into our implementation we loose one of the primary benefits of using the interface. This is the downside of interface based development. If interfaces are poorly designed and have to change or not easy to implement we will not receive the benefits of using them and all they achieve is to make our code more complex. We need to think out the interfaces very carefully at design time before getting too far into the implementation because once they are implemented and made available to other resources they should not change.
Here's a concrete example using the pencil sharpener: The IPencil interface defined earlier could have been an abstract class declared as follows. Imagine if we are working of version 2 of our great pencil-sharpening program and had a directive to modify the class to allow the writing of different strings through the "Write()" method.
public abstract class PencilBase
{
private bool m_isSharp;
private string m_message;
public void Write()
{
Console.Write(m_message);
}
public bool IsSharp { get { return m_isSharp; } }
}
If we change the interface some of our client's code may no longer compile, right? Can you see how the change to the "Write()" interface below is a bad idea:
public abstract class PencilBase
{
private bool m_isSharp;
//private string m_message;
void Write(string message)
{
Console.Write(message);
}
bool IsSharp { get { return m_isSharp; } }
}
Any code referencing the "Write()" method will now be broken by this very simple change and won't compile. I'm sure you can see how, in the context of a very large and complex system, this code is not really maintainable and how any other code referencing it (or reusing it) is taking a big risk because the implementation is not "fixed".
If we see interfaces we know they are there to provide reuse of the underlying implementation and we know we should work around the interface so we don't break referencing code. For example if we look at the code below, we know the PencilBase must adhere to the IPencil interface so we know we can't change the "Write()" implementation without breaking stuff. Now, any developer who understands interface development can also easily maintain our code without breaking it (They could actually re-write all the implementation code as long as they adhere to the interface). The IPencil interface gives everyone involved with the project a roadmap to what can be changed and what should stay the same.
public interface IPencil
{
void Write();
bool IsSharp { get; }
}
public abstract class PencilBase : IPencil
{
private bool m_isSharp;
private string m_message;
public void Write()
{
Console.Write(m_message);
}
public bool IsSharp
{
get { return m_isSharp; }
}
}
Here is one possible way to make the change without breaking the interface by adding a "Message" property to the class.
public abstract class PencilBase : IPencil
{
private bool m_isSharp;
private string m_message;
public string Message
{
get { return m_message; }
set { m_message = value; }
}
public void Write()
{
Console.Write(m_message);
}
public bool IsSharp
{
get { return m_isSharp; }
}
}
A Contract
Another place interfaces really shine when we are defining how two classes should interact. The interfaces for each class act as a contract. This contract should be complete and concise so that anyone reading it can understand and implement the interface and write code that interacts with our interface. Take a look at our IPencil and IPencilSharpener interfaces below. You can see how IPencilSharpenes sharpens an IPencil. This is the "30,000-foot view" and helps us and other developers understand the structure of the code on an abstract level instead of having to dig through thousands of lines of code to understand (or remember) how a PencilSharpener and Pencil class interact and the impacts of any changes to either. If you are trying to understand any code, try getting a handle on the provided interfaces first and you are 90% of the way there.
public interface IPencil
{
void Write();
bool IsSharp { get; }
}
public interface IPencilSharpener
{
void Sharpen(IPencil pencil);
}
The above interfaces are great, but could be improved because they are not really complete. To have a complete interface "contract" between these two objects, we need a way for the IPencil to be notified when it has been sharpened by the IPencilSharpener so the IPencil can set it's "IsSharp" property back to "true". Can you see a way to improve the interface to complete our contracts and still have reusable, maintainable code?
public interface IPencilSharpener
{
void Sharpen(IPencil pencil);
}
public interface IPencil
{
string Message{ get; set; }
bool IsSharp { get; }
void Write();
void OnSharpened();
}
All we really have to do is add a way for the IPencil to be notified when it has been sharpened. (You probably noticed that I've also added a "Message" property to make the IPencil more usable.) Now any other developer could build pencils and/or pencil sharpeners that will interact with ours if they understand the contracts. Here's how we'll implmement the interfaces.
public class Pencil : IPencil
{
#region Constructors
public Pencil(int countBeforeDull)
{
m_countBeforeDull = countBeforeDull;
m_message = string.Empty;
m_charsUsed = 0;
}
#endregion
#region Member Variables
private string m_message;
private int m_countBeforeDull;
private int m_charsUsed;
#endregion
#region IPencil Members
public string Message
{
get { return m_message; }
set { m_message = value; }
}
public bool IsSharp
{
get { return m_countBeforeDull > m_charsUsed; }
}
public void Write()
{
foreach (char c in m_message)
{
if (IsSharp)
Console.Write(c);
else
Console.Write('#');
m_charsUsed++; // used up a character
}
Console.WriteLine();
}
public void OnSharpened()
{
this.m_charsUsed = 0;
}
#endregion
}
public class PencilSharpener: IPencilSharpener
{
#region IPencilSharpener Members
public void Sharpen(IPencil pencil)
{
pencil.OnSharpened();
}
#endregion
}
And here's our executing code:
static voidMain(string[] args)
{
IPencil p = new Pencil(20);
p.Message = "This pencil should not be dulled any time soon";
p.Write();
if (!p.IsSharp)
{
PencilSharpener sharpener = new PencilSharpener();
sharpener.Sharpen(p);
}
p.Message = "That's better";
p.Write();
}
Overcoming a C# Language Limitation
Using C# we can only inherit from one class but can implement any amount of interfaces. Because of this inheritance limitation, we have to be very careful of which class we choose to inherit from because we only get one. This is a place where scalability is negatively impacted. Once we have an inheritance hierarchy set up, it's difficult to change. We can achieve the same results of inheritance through class composition and exposing the functionality of a "wrapped" object through an interface and our classes won't use up our "one shot" inheritance.
For example, if James Bond needed us to develop a pencil, we would have to be very careful how to build it because the requirements will be quite complex: the pencil could possibly be used an underwater breathing apparatus and as a one-shot gun. The pencil also probably blows up if the pencil lead gets too short. Using only inheritance, this would be very difficult without a complex inheritance heriarchy. Using interfaces, it becomes easy. The code below demonstrates how we can indirectly "inherit" from a Pencil through composition and exposing the contained Pencil implementation through an interface.
class BondPencil : IPencil
{
#region Constructors
public BondPencil(Pencil pencil)
{
m_pencil = pencil;
}
#endregion
#region Member Variables
private Pencil m_pencil;
#endregion
#region IPencil Members
public string Message
{
get { return m_pencil.Message; }
set { m_pencil.Message = value; }
}
public bool IsSharp
{
get { return m_pencil.IsSharp; }
}
public void Write()
{
m_pencil.Write();
if (!IsSharp) Console.WriteLine("Blows up killing the bad guy!");
}
public void OnSharpened()
{
m_pencil.OnSharpened();
}
#endregion
}
Now we could also wrap an UnderwaterBeathingApparatus class and it's IUnderwaterBeathingApparatus and a OneShotGun class and its IOneShotGun interface and we effectively have worked around the single inheritance limitation of c#. Notice that we've also added the functionality to the Write() method to blow up bad-guys (the BondPencil is not really meant for writing anyways). Guess what?... Now we're using design patterns and you are looking at the GOF Decorator pattern.
class BondPencil : IPencil, IUnderwaterBreathingApparatus, IOneShotGun
What's Implementing the Interface
When using the BondPencil or regular Pencil, it would be a good idea to do so through the interface. That way we know that our code will not be broken if someone later comes along and changes the classes or if we ever need to us a different type of pencil (like maybe a mechanical pencil). There will be times we want to know what concrete class we are dealing with so we don't blow ourselves up with a BondPencil. There are a couple ways to do this.
1) The 'is' keyword returns a boolean value and is true if the object can be cast as the type.
if (p is BondPencil) return; // GET OUT OF HERE!
2) The 'as' keyword is similar to 'is' but 'as' returns a reference to the object if it can be cast or a null reference if it can not be cast.
if (null != p as BondPencil) return; // GET OUT OF HERE!
3) If we know it is a BondPencil and want to get at some of the other functionality, like the UnderWaterBreathingApparatus, we can force the cast. However, this is not adviseable because if p can not be cast as a BondPencil this operation will throw an InvalidCastException.
(BondPencil) p
Super Pencil
Let's make a mechanical pencil that's self-sharpening. We just need to check that when it needs sharpening, we check that we are sharpening a mechanical pencil, right?
class MechanicalPencil: IPencil, IPencilSharpener
{
#region Constructors
public MechanicalPencil(int countBeforeDull)
{
m_countBeforeDull = countBeforeDull;
m_message = string.Empty;
m_charsUsed = 0;
}
#endregion
#region Member Variables
private string m_message;
private int m_countBeforeDull;
private int m_charsUsed;
#endregion
#region IPencil Members
public string Message
{
get { return m_message; }
set { m_message = value; }
}
public bool IsSharp
{
get { return m_countBeforeDull > m_charsUsed; }
}
public void Write()
{
foreach (char c in m_message)
{
if (IsSharp)
Console.Write(c);
else
{
this.Sharpen(this);
Console.Write("\nSharpening...\n");
}
m_charsUsed++; // used up a character
}
Console.WriteLine();
}
public void OnSharpened()
{
this.m_charsUsed = 0;
}
#endregion
#region IPencilSharpener Members
public void Sharpen(IPencil pencil)
{
if (pencil is MechanicalPencil)
{
pencil.OnSharpened();
}
}
#endregion
}
An even better way is to make sure our MechanicalPencil can only sharpen itself by modifying the "Sharpen()" method:
public void Sharpen(IPencil pencil)
{
if (pencil.Equals(this))
{
pencil.OnSharpened();
}
}
In Summary
As you saw in this article, interfaced based programming is a great way to ensure maintainability, reusability and flexibility in your code. It does take a bit more discipline to carefully define interfaces early in the development cycle, but the payoffs are more than worth it. If the interfaces are kept clear and concise they will not only help other developers understand our code but will also ensure a more stable and flexible code base. Give interfaced-based development a try in your next project, if you follow the basic rules of keeping it simple, you won't regret it.
Until next time,
Happy coding
Steve (Steve9999)Posted May 12, 2018, 2:12 PM
I would like to say interfaces can be abused also. I've been coding for 30 years. Implementing single use interfaces is bad programming in almost all cases in my opinion. If you need to add a simple enhancement that requires an additional parameter to a back end method when you've used interfaces all over you may end up rewriting a large section of code and breaking everything while doing it. Debugging can become a nightmare very quickly. We just let go of a developer who for each windows form implemented an interface, inherited from an abstract class, added presenter classes for both adding and editing which also implemented their own interface. Code would call a method in the presenter class which then would access the interface back in the form. This is pointless and wasteful. We would have developers spend a full day or more fixing simple things and end up hacking it a lot of times by avoiding using the interface all together because it was just too much of a hassle to modify everything. We finally broke down and rewrote the screens and now can fix them or add enhancements in a few minutes. Interfaces are contracts, they're not intended to change. Don't create one time use interfaces, don't make your code spaghetti code by creating something that could be done just as easily in a single method. You want separation of code, great! Create a separate class that does what you want and call methods in it. You want to insure something isn't going to change behind you back in the future or need to insure the objects you're getting ready use supports a common property or method that you know isn't going to change, great! Then create an interface.
Ramesh PalaniappanPosted Aug 26, 2016, 10:11 AM
Nice
kalu singh raoPosted Jul 9, 2016, 12:12 PM
Nice...
Łukasz KowalikPosted Oct 29, 2015, 3:34 AM
could you recommend any book in that topic?
Ravi KumarPosted Oct 8, 2013, 6:01 AM
nice article..
Justin JonesPosted May 31, 2012, 3:23 PM
Excellent Tutorial. Thanks.
Smart ManeditedPosted May 14, 2012, 8:20 AMEdited May 14, 2012, 8:22 AM
public List<GameState> Players = new List<GameState>(); //this code can't be applied //What is the alternative code for this in Interface System.Collections.Generic.List //Error = Interfaces cannot contains fields
Chetan AgarwalPosted Mar 20, 2012, 2:38 AM
Great article. Exactly contains information to understand interfaces, nothing more (complex) nothing less...perfect. Thanks for sharing this article. This helped me understand basics of interfaces.
Cob RacuseditedPosted Apr 13, 2011, 8:14 PMEdited Apr 13, 2011, 8:17 PM
This is another example of inheritance. The same results can be obtained by omitting the interfaces and moving the methods to the classes proper. I say this because everything is in one namespace and all the code code is recompiled before every run. If you really want to prove the contract idea, make all the classes in to DLLs. Sharpener must not reference an interface type, but simply have the method Sharpe. The Pencil classes should also be DLLs and not reference an interface. Furthermore Pencil classes should not call the Sharpen method, but should use another name. The problem then becomes one of creating a wrapper or bridging class that uses an interface type to map the call from Pencil to the Sharpen method of the Sharpener cl;ass. Only this wrapper/bridging class is permitted to have its code changed and recompiled.
Harish BansalPosted Jan 12, 2011, 2:50 AM
Now! I know that what is the role of interface in development. :)
thawat tongtamPosted Jun 2, 2010, 11:58 PM
Good
rajender chPosted May 12, 2010, 6:25 AM
Very Good Article.
Theo MedeirosPosted Jan 2, 2010, 11:36 AM
"The code below demonstrates how we can indirectly "inherit" from a Pencil through composition" I believe you meant to say "through aggregation".
Arati RPosted Dec 7, 2009, 3:30 AM
Good article. But i want to know when all the methods are public in the class which implements Interface,then why need to implement interface.This can be done without the interface,then why need interfaces?
bharat parmarPosted Mar 14, 2009, 3:23 AM
hello sir give me example of embedded prongraming in c#
Bart de BoerPosted Dec 10, 2008, 7:50 AM
Hi, Good article, but I couldn't happen to overlook the Equals you use to check 'identity' - as surely you know Equals is meant to test equality and not same identity. Of course, the default behavior is the same but the IPencil you get in Sharpen() could have been overridden and test equality by testing just, say, Message and 'IsSharp' and you'd be sharpening another pencil..... Bart de Boer
MarnochPosted Feb 10, 2008, 9:41 PM
I know this has been up for quite some time but I certainly appreciate your efforts. Interfaces have always been an enigma to me but I think I have a handle on it now. Thanks Again
No JunkPosted Oct 23, 2007, 5:02 PM
In the code block that follows, Pencil is typed as IPencil and PencilSharpener is just typed as PencilSharpener. Why is PencilSharpener not typed as IPencilSharpener. Thank you! static void Main(string[] args) { IPencil p = new Pencil(20); p.Message = "This pencil should not be dulled any time soon"; p.Write(); if (!p.IsSharp) { PencilSharpener sharpener = new PencilSharpener(); sharpener.Sharpen(p); } p.Message = "That's better"; p.Write(); }
ramanujamPosted Sep 20, 2007, 5:18 AM
How Interfaces helps in Events
jaime godoyPosted Jul 19, 2007, 12:56 PM
Hi ! I am of Chile You have an example of WebControls ???
jaime godoyPosted Jul 19, 2007, 12:56 PM
Hi ! I am of Chile You have an example of WebControls ???
jaime godoyPosted Jul 19, 2007, 12:56 PM
Hi ! I am of Chile You have an example of WebControls ???
christo pherPosted May 8, 2007, 1:34 AM
goo articles
Sarbjit ChawlaPosted Oct 16, 2006, 3:29 PM
Hey, I know you are trying to use the concept of COM here which is inturn you wanted to explain COM. Yes, you are right we should follow this type of client/server contract using Interfaces as we use to do in COM defining Libraries and Interfaces in the IDL files. We use to put the implementation in the seperate class files and use base class pointers to point any of the derived classes. Can you send me a sample code to proove client/server contract. I will change the implementation and compile the code, and will not compile the client and will check how it will work.
jamshid hashimiPosted Apr 7, 2006, 6:08 AM
Good, Thanks for sharing
YogeshPosted Apr 7, 2006, 1:34 AM
Very Good article