Why We Use Interfaces in C#

In this article I will not cover anything about interfaces since I have already covered why interfaces exist and what interfaces are. As soon as we learn about interfaces, we think that it was so easy to be familiar with the interfaces and now at least I can understand a very important concept of C#. But as soon as someone asks where should I use interfaces, we start doubting our knowledge about the interfaces and the only example we can provide is that C# doesn’t support multiple inheritance and that is why we use interfaces. As long as we are not sure of why to use interfaces, our knowledge about them is incomplete.

The article is published here on my blog.

I have developed a small application here that would help us to understand the utilization of interfaces.

Project Task: The client would use here a Driver (class) to drive a Car (class).

Concepts covered: The following are the concepts that we would cover in this application that in turn would help us to understand about interfaces. 

  1.       Extensibility
  2.       Implementation Hiding
  3.       Accessing object using interfaces
  4.       Loose Coupling. 

Before stating the discussion about the code, I would like to take you through the various components of the project as shown in the following figure: 

  

InterfaceUtilization is the client that would be using the interface components to satisfy the requirements for the task. The client only contains the references for the Interfaces and Factory namespaces.

Car and Driver are the assemblies that contain the classes that implements the ICar and IDriver interfaces from the Interfaces namespace. These classes are the entities whose instances would be used to satisfy the requirements.

Interfaces is the namespace that contains the contracts or interfaces that would be in turn be implemented by the individual classes (in our class Car and Driver).

Factory is the assembly that is used by the client (InterfaceUtilization) to create and return the instances of the entities (Car and Driver). Factory has the references of the Car and Driver as well as the Interfaces namespaces.

Now I would like to explain all the points here one by one that I have stated earlier.


1.
Extensibility

We can satisfy extensibility using the interfaces in C#. In this example I have two interfaces, ICar and IDriver, that are implemented by NormalCar, RaceCar and Driver, RaceDriver respectively. We can easily extend the interfaces to create new classes that implement the same contract functionalities.

Assume I want to add a new car type apart from which are shown in the preceding figure as shown below:
 

  1. public class VintageCar: ICar   
  2. {  
  3.     private string modelName;  
  4.     public VintageCar(string modelName)   
  5.     {  
  6.         MoodelName = modelName;  
  7.     }#region ICar Members  
  8.     public string MoodelName   
  9.     {  
  10.         get   
  11.         {  
  12.             return modelName;  
  13.         }  
  14.         set   
  15.         {  
  16.             modelName = value;  
  17.         }  
  18.     }  
  19.     public void DriveCar(IDriver driver)   
  20.     {  
  21.         if (driver.YearsOfExperience > 10) driver.Drive();  
  22.     }#endregion  
  23. }  

And to get an instance of this car type I need to add a new factory method to the factory class as shown below:

  1. public static ICar CreateVintageCar(string modelName)    
  2. {  
  3.   
  4.    return new VintageCar(modelName);  
  5.   
  6. }  

Now to use this new car type, in the client we just need to call the preceding method of factory as shown below:

  1. IDriver myDriver= Factory.Factory.CreateDriver("vikram", 38, 5);  
  2.   
  3. ICar vintageCar = Factory.Factory.CreateNormalCar("Old Toyota");  
  4.   
  5. vintageCar.DriveCar(myDriver);  

From the preceding example we can see that we easily extend a specific interface without having much trouble since an out interface already contains the necessary data member and member functions that are need for a specific type.

2. Implementation Hiding

Our client code doesn’t know anything about the implementation details of both the Driver and Car class, in this we can see that the implementation is known to the client. Here the factory class takes care of creating instances of the classes for the client.

That is why the client knows only about the Interface and Factory namespaces.

3.
Accessing object using interfaces

If we are using classes derived from the interface then there is no need to create the class instances that we can create variables of the specific instance type that in turn will contain the referenc of the type that implements that specific interface. And this variable of interface type can be used as a parameter and that specific function can use that reference to satisfy the requirements. As we can see in the following example, I have a function of VintageCar that expects a parameter of type IDriver interface and in turn use this variable to work on the class reference.

  1. public void DriveCar(IDriver driver)  
  2. {  
  3.   
  4.    //years of exprience need to be more to hanle this car type  
  5.   
  6.    if (driver.YearsOfExperience > 20)  
  7.   
  8.    driver.Drive();  
  9.   
  10. }  

This feature helps us to treat various classes as the same type of interface. In other words, I can create a variable of any type implementing IDriver and pass as an argument to the DriveCar method.

  1. IDriver myDriver= Factory.Factory.CreateDriver("vikram", 38, 5);  
  2.   
  3. ICar vintageCar = Factory.Factory.CreateNormalCar("Old Toyota");  
  4.   
  5. vintageCar.DriveCar(myDriver); // prints "Cannot drive this car"  
  6.   
  7. IDriver raceDriver = Factory.Factory.CreateRaceDriver("myname", 40, 20);  
  8.   
  9. vintageCar.DriveCar(raceDriver); // prints "myname is driving a race car";  

       4. Loose Coupling

As said in the previous point, only an interface type variable can be passed as an argument that is again helpful to achieve loose coupling. Before explaining this concept please has a look at the code snippet below.

  1. public interface ICar  
  2. {  
  3.   
  4.    string MoodelName { getset; }  
  5.   
  6.    void DriveCar(IDriver driver);  
  7.   
  8. }  

What we can derive from the preceding code snippet is that any class that would be implementing an ICar interface would have a definition of DriveCar method that takes IDriver as parameter. Now having an interface type as parameter gives us ability to provide an argument of the class instance that derives from the IDriver interface for this function. On the other side, if the parameter would have been any class type variable it would have been difficult to satisfy that requirement.

Though the preceding code can be implemented using much better designing principles, like better use of factory pattern but that was not my main concern to write this article.

Please find the solution code for the blog attached here.


Similar Articles