DEV Community

Sam Walpole
Sam Walpole

Posted on • Edited on

Top 3 Reasons to use Interfaces in Your Code

What is an interface?

An interface is a common feature to many programming languages that allows you to define the public properties and methods that a class should expose, without having to define how they are implemented. You can then tell a class to use that interface and that class will contain the actual implementation.

In other words, the interface defines the what, and the class defines the how.

As a silly example (here I'm using C#), we will define a LasagneChef interface, ILasagneChef, that contains the method CookLasagne.

public interface ILasagneChef
{
    Lasagne CookLasagne();
}
Enter fullscreen mode Exit fullscreen mode

We can then create a class that implements the IChef interface:

public class ChefGordon : ILasagneChef
{
    public Lasagne CookLasagne()
    {
        // Gordon's lasagne recipe

        return lasagne;
    }
}
Enter fullscreen mode Exit fullscreen mode

And that's all it takes to define and implement an interface. It might not be clear yet why this is useful. Hopefully the examples below will convince you why you need to start using interfaces in your code.

Reason 1: Creates Loosely Coupled Code

Loose coupling means that each component in your code should not be aware of how any other component of the system works, or be dependant on any concrete implementation. This makes it easier to build and maintain code because it means that you can be confident that changing one component in your code should not cause problems in other areas of your code.

As an example of tightly coupled code, lets define a restaurant class that is dependant on ChefGordon as the lasagne chef, and has an OrderLasagne method.

public class Restaurant
{
    public ChefGordon LasagneChef { get; }   

    public Restaurant(ChefGordon lasagneChef)
    {
        this.LasagneChef = lasagneChef;
    } 

    public Lasagne OrderLasagne()
    {
        var lasagne = this.LasagneChef.CookLasagne();

        return lasagne;
    }
}
Enter fullscreen mode Exit fullscreen mode

Elsewhere in our code, we could place a lasagne order with the following code:

var chef = new ChefGordon();
var restaurant = new Restaurant(chef);
var lasagne = restaurant.OrderLasagne();
Enter fullscreen mode Exit fullscreen mode

This code will work fine as long as ChefGordon is the lasagne chef for the restaurant. However, imagine that we replaced Chef Gordon with ChefJamie as the LasagneChef for the restaurant. Now our external code will break because it is still expecting the restaurant to use ChefGordon as the lasagne chef.

This is a trivial example, but hopefully you can that in more complicated systems, having to change code elsewhere in the codebase every time you make a small change in your code will make the code quite unmanageable.

The solution to this is to use interfaces to design loosely coupled code.

In our example, if we instead make the restaurant dependant on the ILasagneChef interface, suddenly in no longer matters which chef we use, as long as that chef implements the ILasagneChef interfaces.

public class ChefGordon : ILasagneChef
{
    public Lasagne CookLasagne()
    {
        // Gordon's lasagne recipe

        return lasagne;
    }
}
public class ChefJamie : ILasagneChef
{
    public Lasagne CookLasagne()
    {
        // Jamie's lasagne recipe

        return lasagne;
    }
}
public class Restaurant
{
    public ILasagneChef LasagneChef { get; }

    public Restaurant(ILasagneChef lasagneChef)
    {
        this.LasagneChef = lasagneChef;
    }

    public Lasagne OrderLasagne()
    {
        var lasagne = this.LasagneChef.CookLasagne();

        return lasagne;
    }
}
var gordon = new ChefGordon();
var jamie = new ChefJamie();

var restaurant1 = new Restaurant(gordon);
var restaurant2 = new Restaurant(jamie);

var lasagne1 = restaurant1.OrderLasagne();
var lasagne2 = restaurant2.OrderLasagne();
Enter fullscreen mode Exit fullscreen mode

And this makes sense. When we place an order for some lasagne, we don't care about the details of how the lasagne is made or by which chef, as long as we get what we want, our lasagne.

Reason 2: Improves Testability

Writing good tests is an essential skill in software development as it allows you to ensure that each part of your system is behaving like it is supposed to. Good tests are particularly important in large systems, as it will help instantly highlight if any code changes have broken a particular part of the system.

One important consideration when writing good tests is to make sure that you are testing the component in isolation, without any dependencies, so that you know that results from the test are entirely due to the component being tested and not due to any problems in that components dependencies.

If all of a components dependencies are interfaces, instead of classes, then it is very easy to make mock implementations of those interfaces that will always give you a reliable, consistent result.

For example, if we wanted to test that the Restaurant OrderLasagne method was behaving properly, we could easily create a mock class of the ILasagneChef interface.

var mock = new Mock<ILasagneChef>();
mock.Setup(m => m.CookLasagne()).Returns(new Lasagne());
var mockLasagneChef = mock.Object;
var restaurant = new Restaurant(mockLasagneChef);
var lasagne = restaurant.OrderLasagne();
Enter fullscreen mode Exit fullscreen mode

If our tests fail, we know that the problem is in the OrderLasagne method, since our mock ILasagneChef always returns a lasagne.

Reason 3: Simplifies Development

This may be last in the list, but it by far my favourite part of using interfaces in my code.

As you are developing a project, there are many different aspects that need to be considered and coded for. For example, there is the User Interface, the logic, the data access etc. Trying to keep track of all of these different aspects at once makes the whole experience confusing, frustrating and you too easily get caught up in little details that could easily change as the project moves forward.

That's why I prefer to focus first on what I want to achieve, and then fill in the details of how to achieve it later. Of course, this can be achieved using interfaces.

For example, I might want to create a web app that displays a list of images of my favourite lasagnes. For simplicity, this could be split into two distinct components, getting the data and displaying the data.

I want to focus first on displaying the data and worry later about how I will actually get the data. So I'll define the following interface:

public interface ILasagneRepository
{
    List<Lasagne> GetLasagneImages();
}
Enter fullscreen mode Exit fullscreen mode

Then I can design my User Interface using this interface.

@inject ILasagneRepository repository
@{
    var lasagneImages = repository.GetLasagneImages();
}
<ul>
@foreach(var image in lasagneImages)
{
    <li>
        <img src="@image.src" />
    </li>
}
</ul>
Enter fullscreen mode Exit fullscreen mode

Once I've written all the code for my user interface, I can then think about how I'll actually get the data by creating a class that implements the ILasagneRepository interface.

public class DatabaseLasagneRepository : ILasagneRepository
{
    public List<Lasagne> GetLasagneImages()
    {
        // code to get images from database
        return lasagneList;
    }
}
Enter fullscreen mode Exit fullscreen mode

Summary

My top 3 reasons to use interfaces in your code are:

  • Reason 1: Creates Loosely Couple Code
  • Reason 2: Improves Testability
  • Reason 3: Simplifies Development

I hope that this has convinced you of the benefits of using interfaces in your code and to start using them in your own projects.

If you enjoyed this read, please follow me here or on twitter: @dr_sam_walpole If you want, you can also buy me a coffee ! 😊

Top comments (0)