Interfaces vs Interfaces

By Vladimir Khorikov

Today, I’d like to discuss the differences between interfaces, abstractions and .NET interfaces, as well as what the term “implementation details” means.

Abstractions

Since .NET came out, the interface language feature it introduced slowly captured the term “Interface”. Nowadays, the C# keyword “interface” is often taken for the only possible way to introduce an API. The terms “Interface” and “.NET Interface” are used interchangeably by many.

Moreover, it is often considered a bad practice to work with classes directly, especially in the case of Dependency Injection:

public Service(IPersonRepository repository) // “Good” practice

{

}

 

public Service(PersonRepository repository) // “Bad” practice

{

}

But is that really the case? To answer this question, we need to step back and recall what the term “Interface” really means.

API is a set of functions client code calls in order to use the functionality a class introduces. There’s a simple thing that differs interface of a class from its implementation details, and that is “public” (and also “internal”) keyword. Every time you add a public method to a class, you change its interface. And every time you call a public method, you use the API of a class.

While .NET interfaces do represent API, i.e. something you can code against, it is incorrect to attribute the term “Interface” exclusively to them. Every class has its own interface that is defined as a set of public methods.

Always depend upon abstractions, not implementations.

I guess this well-known phrase from the Dependency inversion principle added a fair part to the overall confusion. Often, .NET interfaces are used to introduce an abstraction, but the truth is, .NET interfaces don’t automatically make your entities abstract. Abstraction is something that depends on the context it is being used in. .NET interfaces, on the other hand, is just a language feature which can be used – along with the other language features – to introduce an abstraction.

There’s virtually no difference between using a .NET interface and a class. If your code depend on a .NET interface, it doesn’t mean it depends on abstraction. Furthermore, a dependency on a class doesn’t automatically make your code dependent on implementation details.

Relationship between .NET interfaces, classes, API and implementation details

API, .NET interfaces, classes and implementation details

API, .NET interfaces, classes and implementation details

This diagram shows the relationships between those notions. .NET interfaces and classes that are well-defined have a solid and clean interface which make them a good abstraction of the concept they describe.

On the other hand, poorly designed classes tend to expose their internal structure to the clients and thus break encapsulation principles. As for poorly designed .NET interfaces, there’s actually a special term: Header interfaces. It means that, rather than introducing some high-level cohesive set of methods that belong to a specific role, the interface just mimics the structure of a class. And, of course, whether or not a class or .NET interface is designed poorly, depends on the context.

.NET Interfaces and YAGNI

While it’s true that, from a client code perspective, there’s no difference in using a .NET interface or a class, it is not always the case if we look at the whole picture.

How often do you see an interface with a single implementation? Frequently, they are created mechanically just for the sake of “loose coupling”:

public interface IPersonRepository

{

    void Save(Person person);

    Person GetById(int id);

}

 

public class PersonRepository : IPersonRepository

{

    public void Save(Person person)

    {

        /* … */

    }

 

    public Person GetById(int id)

    {

        /* … */

    }

}

But the thing is, just as with abstractions, using a .NET interface doesn’t automatically make your code more decoupled. Also, such approach contradicts YAGNI principle. Adding a new .NET interface should always be justified. Creating a .NET interface with a single implementation is a design smell.

The last thing I’d like to mention is unit testing. There’s some uncertainty in the question of whether or not the creation of .NET interfaces for the purpose of mocking is a smell. I personally tend to avoid such situations, but it’s not always possible. I’ll try to describe this topic in a future post.

Summary

  • The term “Interface” != .NET interface. Depending on a .NET interface doesn’t mean your code depend on an abstraction.
  • Class != implementation details. Depending on a class doesn’t make your code dependent on implementation details.
LinkedInRedditTumblrBufferPocketShare




  • Erik

    The term interface did not come about with .NET or C#. And, in fact, interfaces are based on Java Interfaces. What’s more, COM/OLE had interfaces going back to the early 90’s using IDL (Interface Definition Language). IUnknown is the most well known unknown ever 😉 so C# has not cornered the market on Interfaces or the use of the term as it’s alive and well in all languages.

    As far as single implementation interfaces and YAGNI, you typically ARE going to need it, otherwise you wouldn’t be doing interface based programming. SOLID priniciples dictate that interface based design creates more loosely coupled code. Yes, it’s possible to create tightly coupled code using interfaces, but these are exceptions rather than rules.. just like it’s possible to write assembly language code that is slower than interpreted basic. Just because a thing can be abused in some situations doesn’t mean you should dismiss it or avoid it.

    Interfaces enable Unit Testing, Loose Coupling, Dependency Injection (the principle, not the container), and multiple (interface) inheritance. It’s true you can do some of those things with abstract classes as well, and really at their heart, interfaces are nothing more than special case abstract classes… Yes, it’s ridiculous to say that because your class implements an interface, it’s an abstraction. That doesn’t even make logical sense. An abstraction is a concept.
    You seem to set up a number of strawmen in this article, and then proceed to knock them all down. Half of what you argue isn’t even a thing…

    You are also conflating “interface” with “contract”. When you add a public method, you change the contract, which by transitive property does change the interface but only in the same way as adding an object to a collection changes the collection. An interface is a “container” of methods, so adding or removing them changes the contents of the container. The contract is the real issue with what has changed, you’ve broken the contract and thus broken any code that uses it.

    An API is a collection of 1 or more Interfaces, so In the same way that adding, removing, or changing methods from an interface breaks contract, so can adding, removing or changing interfaces from an API, although typically adding interfaces is much less problematic. It’s all a contract, however. A formal declaration of how these interfaces will be used. Contracts are more important, because they also define *behavior* in addition to type.

    The L in SOLID, Liskov Substitution Principle is all about the contract.

    But, I digress. Your article misses the point on a lot of the concepts of the stuff you argue about. So much so, I’m kind of shocked actually.

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      Indeed, the term didn’t come from .NET, and I didn’t state it in the post. Nevertheless, I do think that in the .NET world at least, that language feature has overtaken the initial concept of an interface.

      Regarding the YAGNI principle. You make the exact point I argue against in the article. Using interfaces (in the .NET sense) doesn’t automatically enable Unit Testing, Loose Coupling or DIP. For example, the use of header interfaces doesn’t make your code more decoupled. Moreover, it makes it less cohesive.

      The topic of contracts is a big one and I didn’t touch it here initially. While I agree with the most of what you wrote about them, there’s one thing I strongly disagree with:

      >Contracts also define *behavior* in addition to type.

      They don’t. Contracts define what the clients get from the method (postconditions) and what they have to do in order to get it (preconditions). They don’t define the actual behavior of the class; client code shouldn’t know what exactly happens under the cover. Doing so would violate the class encapsulation.

  • Bob

    “Class != implementation details. Depending on a class doesn’t make your code dependent on implementation details.”
    Yes. But from that moment that part of the code will be dependent on that specific class and it won’t be replaceable anymore easily.
    ” When the class creates a dependency using a concrete class , this brings the dependency wholesale into any tests you write, as test doubles (such as mocks) can’t be created to represent their behaviour. ”
    from: https://blogs.endjin.com/2014/04/understanding-dependency-injection/

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      Bob, thank you for writing.
      You are talking about two topics that can be discussed independently.

      >”But from that moment that part of the code will be dependent on that specific class and it won’t be replaceable anymore easily.”

      There’s nothing wrong in being dependent on a specific class. In fact, your code depends on them all over your code base (string, DateTime, etc.). Depending on an interface doesn’t automatically make the dependency itself easy to replace. It’s a well-chosen abstraction that makes it easier to maintain it, not a .NET interface (due to .NET interface != abstraction).

      >”test doubles (such as mocks) can’t be created to represent their behaviour”

      I agree with that one. Nevertheless, changing a class to a .NET interface doesn’t make your code less or more dependent on implementation details. It’s just a compulsory measure which helps unit test your code.

      • Harry McIntyre

        Testability isn’t a problem if the methods and properties are virtual, the SDK designer just needs to remember to mark them up (unlike Java).

        That’s not to say that instances shouldn’t be created using an injected factory (even if it’s just a Func).

        • http://enterprisecraftsmanship.com/ Vladimir Khorikov

          You are right, virtual members help solve the problem. But many developers still tend to use interfaces for the sake of testability, and I think it makes sense in terms of separating the unit-testing artifacts from the original class.

          I guess if C# would have members virtual by default (just like Java), more developers would choose the other approach.