What Makes Good Code? – Should Every Class Have An Interface? Pt 1

I mentioned in the first post of this series that I’ll likely be referring to C# in most of these posts. I think the concept of an interface in C# extends to other languages–sometimes by a different name–so the discussion here may still be applicable. Some examples in C++, Javaand Python to get you going for comparisons.

Please note: This post is quite dated and I would recommend you head over to this article I’ve written on C# and interfaces.

From MSDN:

An interface contains definitions for a group of related functionalities that a class or a struct can implement.
By using interfaces, you can, for example, include behavior from multiple sources in a class. That capability is important in C# because the language doesn’t support multiple inheritance of classes. In addition, you must use an interface if you want to simulate inheritance for structs, because they can’t actually inherit from another struct or class.

It’s also important to note that an interface decouples the definition of something from its implementation. Decoupled code is, in general, something that programmers are always after. If we refer back to the points I defined for what makes good code (again, in my opinion), we can see how interfaces should help with that.

  • Extensibility: Referring to interfaces in code instead of concrete classes allows a developer to swap out the implementation more easily (i.e. extend support for different data providers in your data layer). They provide a specification to be met should a developer want to extend the code base with new concrete implementations.
  • Maintainability: Interfaces make refactoring an easier job (when the interface signature doesn’t have to change). A developer can get the flexibility of modifying the implementation that already exists or creating a new one provided that it meets the interface.
  • Testability: Referring to interfaces in code instead of concrete classes allows mocking frameworks to leverage mocked objects so that true unit tests are easier to write.
  • Readability: I’m neutral on this. I don’t think interfaces are overly helpful for making code more readable, but I don’t think they inherently make code harder to read.

I’m only trying to focus on some of the pros here, and we’ll use this sub-series to explore if these hold true across the board. So… should every class have a backing interface?

An Example of an Interface in C#

Let’s walk through a little example. In this example, we’ll look at an object that “does stuff”, but it requires something that can do a string lookup to “do stuff” with. We’ll look at how using an interface can make this type of code extensible!

First, here is our interface that we’ll use for looking up strings:

public interface IStringLookup
{
    string GetString(string name);
}

And here is our first implementation of something that can lookup strings for us. It’ll just lookup an XML node and pull a value from it. (How it actually does this stuff isn’t really important for the example, which is why I’m glossing over it):

public sealed class XmlStringLookup : IStringLookup
{
    private readonly XmlDocument _xmlDocument;

    public XmlStringLookup(XmlDocument xmlDocument)
    {
        _xmlDocument = xmlDocument;
    }

    public string GetString(string name)
    {
        return _xmlDocument
            .GetElementsByTagName(name)
            .Cast<XmlElement>()
            .First()
            .Value;
    }
}

This will be used to plug into the rest of the code:

private static int Main(string[] args)
{
    var obj = CreateObj()
    var stringLookup = CreateStringLookup();

    obj.DoStuff(stringLookup);

    return 0;
}

private static IMyObject CreateObj()
{
    return new MyObject();
}

private static IStringLookup CreateStringLookup()
{
    return new XmlStringLookup(new XmlDocument());
}

public interface IMyObject
{
    void DoStuff(IStringLookup stringLookup);
}

public class MyObject : IMyObject
{
    public void DoStuff(IStringLookup stringLookup)
    {
        var theFancyString = stringLookup.GetString("FancyString");

        // TODO: do stuff with this string
    }
}

In the code snippet above, you’ll see our Main() method creating an instance of “MyObject” which is the thing that’s going to “DoStuff” with our XML string lookup. The important thing to note is that the DoStuff method takes in the interface IStringLookup that our XML class implements.

Let’s Make The C# Interface Magic Happen

Now, XML string lookups are great, but let’s show why interfaces make this code extensible. Let’s swap out an XML lookup for an overly simplified CSV string lookup! Here’s the implementation:

public sealed class CsvStringLookup : IStringLookup
{
    private readonly StreamReader _reader;

    public CsvStringLookup(StreamReader reader)
    {
    _reader = reader;
    }

    public string GetString(string name)
    {
        string line;
        while ((line = _reader.ReadLine()) != null)
        {
            var split = line.Split(',');
            if (split[0] != name)
            {
                continue;
            }

            return split[1];
        }

        throw new InvalidOperationException("Not found.");
    }
}

Now to leverage this class, we only need to modify ONE line of code from the original posting! Just modify CreateStringLookup() to be:

private static IStringLookup CreateStringLookup()
{
    return new CsvStringLookup(new StreamReader(File.OpenRead(@"pathtosomefile.txt")));
}

And voila! We’ve been able to extend our code to use a COMPLETELY different implementation of a string lookup with relatively no code change. You could make the argument that if you needed to modify the implementation for a buggy class as long as you were adhering to the interface, you wouldn’t need to modify very much of the surrounding code (just like this example). This would be a point towards improved maintainability in code.

Wrapping Up The C# Interface Example

“But wait!” you shout, “I could have done the EXACT same thing with an abstract class instead of the IStringLookup interface you big dummy! Interfaces are garbage!”

And you wouldn’t be wrong about the abstract class part! It’s totally true that IStringLookup could instead have been an abstract class like StringLookupBase (or something…) and the benefits would still apply! That’s a really interesting point, so let’s keep that in mind as we continue on throughout this whole series. The little lesson here? It’s not the interface that gives us this bonus, it’s the API boundary and level of abstraction we introduced (something that does string lookups). Both an interface and abstract class happen to help us a lot here.

Continue to Part 2

author avatar
Nick Cosentino Principal Software Engineering Manager
Principal Software Engineering Manager at Microsoft. Views are my own.

This Post Has 5 Comments

  1. Scott MacLellan

    Hello Nick!

    I am excited about this series and where you take it.

    I have been thinking about how abstractions and unnecessary layering can impede readability.

    To take on your title, I don’t think every class should have an interface. Often interfaces have a single implementation which will never be replaced. They can still be useful for reasons you have indicated (testing, decoupling, etc.), but I don’t think the default should be to include them everywhere.

    In one codebase I work in they have went CRAZY with interfaces for everything. Following how the code fits together is much harder. Understanding what the behaviour is without debugging the code is a challenge. At this extreme reading the code is much harder. The extra interfaces get in the way.

    You might like this blog post: https://lostechies.com/derickbailey/2008/10/20/dependency-inversion-abstraction-does-not-mean-interface/

    While an interface is a convenient abstraction, it isn’t the only tool for the job and can be abused. Choosing when to use interfaces or another abstractions and when not to use them is part of the fun of being a developer. As with anything I think there is a better balance to be found.

    1. ncosentino

      Hey Scott!

      Thanks for the comment, and I’m glad you enjoyed the post! I’m excited to write a bit more about these topics because they’re questions I’ve been asking myself *AND* they come up in the workplace too.

      As with most things, I usually don’t believe the best answer is to any one extreme, but rather somewhere in the middle. With these posts, I’m hoping to try approaching them from an extreme perspective (i.e. all classes must have interfaces) to try and prove that putting strict rules in place that apply to all of your code can often make the code worse.

      I’ve set out in one of my hobby projects to take a lot of the practices I’ll be talking about in this series to one extreme or another. In the beginning, the rules felt great to have in place… but after writing enough code in one codebase, some of the rules seem to be inhibiting adding more code. Kind of ironic 🙂

      Great Los Techies post! It aligns with what you were saying about not “the only tool for the job”, and I certainly echo that!

      Thanks again for the comment, and happy new year!

      1. Scott MacLellan

        That sounds like a great way to learn! Best of luck with the extremes and I hope those extra inhibitions make it to another post.

        Later.

Leave a Reply