One of the first sound bites from the beloved book Design Patterns is this:

Program to an interface, not an implementation

It would seem that a corollary is that we can measure the quality of our code on the number of interfaces; the more, the better. However, that's not how it feels in reality when you are trying to figure out whether to use an IFooFactory, IFooPolicy, IFooPolicyFactory or perhaps even an IFooFactoryFactory.

Do you extract interfaces from your classes to enable loose coupling? If so, you probably have a 1:1 relationship between your interfaces and the concrete classes that implement them. That's probably not a good sign, and violates the Reused Abstractions Principle (RAP). I've been guilty of this and didn't like the result.

Having only one implementation of a given interface is a code smell.

Programming to an interface does not guarantee that we are coding against an abstraction. Interfaces are not abstractions. Why not?

An interface is just a language construct. In essence, it's just a shape. It's like a power plug and socket. In Europe we use one kind, and the US uses another, but it's only by convention that we transmit 230V through European sockets and 110V through US sockets. Although plugs only fit in their respective sockets, nothing prevents us from sending 230V through a US plug/socket combination.

Krzysztof Cwalina already pointed this out in 2004: interfaces are not contracts. If they aren't even contracts, then how can they be abstractions?

Interfaces can be used as abstractions, but using an interface is in itself no guarantee that we are dealing with an abstraction. Rather, we have the following relationship between interfaces and abstractions:

Abstractions, interfaces and their intersection

There are basically two sets: a set of abstractions and a set of interfaces. In the following we will discuss the set of interfaces that does not intersect the set of abstractions, saving the intersection for another blog post.

There are many ways an interface can turn out to be a poor abstraction. The following is an incomplete list:

LSP Violations

Violating the Liskov Substitution Principle is a pretty obvious sign that the interface in use is a poor abstraction. This may be most obvious when the consumer of the interface needs to downcast an instance to properly work with it.

However, as Uncle Bob points out, even an interface as simple as this seemingly innocuous rectangle ‘abstraction' contains potential dangers:

public interface IRectangle
{
    int Width { get; set; }
    int Height { get; set; }
}

The issue becomes apparent when you attempt to let a Square class implement IRectangle. To protect the invariants of Square, you can't allow the Width and Height properties to differ. You have a couple of options, none of which are very good:

  • Update both Width and Height to the same value when one of them are being written.
  • Ignore the write operation when the caller attempts to assign an invalid value.
  • Throw an exception when the caller attempts to assign a Width which is different from the Height (and vice versa).

From the point of view of a consumer of the IRectangle interface, all of these options would at the very least violate the Principle of Least Astonishment, and throwing exceptions would definitely cause the consumer to behave differently when consuming Square instances as opposed to ‘normal' rectangles.

The problem stems from the fact that the operations have side effects. Invoking one operation changes the state of a seemingly unrelated piece of data. The more members we have, the greater the risk is, so the Interface Segregation Principle can, to a certain extent, help.

Header Interfaces

Since a higher number of members increases the risk of unexpected side effects and temporal coupling it should come as no surprise that interfaces mechanically extracted from all members of a a concrete class are poor abstractions.

As always, Visual Studio makes it very easy to do the wrong thing by offering the Extract Interface refactoring feature.

We call such interfaces Header Interfaces because they resemble C++ header files. They tend to simply state the same thing twice without apparent benefit. This is particularly true when you have only a single implementation, which tends to be very likely for interfaces with many members.

Shallow Interfaces

When you use the Extract Interface refactoring feature in Visual Studio, even if you don't extract every member, the resulting interface is shallow because it doesn't recursively extract interfaces from the concrete types exposed by the extracted members.

An example I've seen more than once involves extracting an interface from a LINQ to SQL or LINQ to Entities context in order to define a Repository interface. As an example, here's an interface extracted from a very simple LINQ to Entities context:

public interface IPostingContext
{
    void AddToPostings(Posting posting);
    ObjectSet<Posting> Postings { get; }
}

At first glance this may look useful, but it isn't. Even though it's an interface, it's still tightly coupled to a specific object context. Not only does ObjectSet<T> reference the Entity Framework, but the Posting class is defined by a very specific, auto-generated Entity context.

The interface may give you the impression of working against loosely coupled code, but you can't easily (if at all) implement a different IPostingContext with a radically different data access technology. You'll be stuck with this particular PostingContext.

If you must extract an interface, you'll need to do it recursively.

Leaky Abstractions

Another way we can create problems for ourselves is when our interfaces leak implementation details. A good example can be found in the SystemWrapper project that provides extracted interfaces for various BCL types, such as System.IO.FileInfo. Those interfaces may enable mocking, but we shouldn't expect to ever be able to create another implementation of SystemWrapper.IO.IFileInfoWrap. In other words, those interfaces aren't very useful.

Another example is this attempt at defining a Repository interface:

public interface IFooRepository
{
    string ConnectionString { get; set; }
    // ...
}

Exposing a ConnectionString property strongly indicates that the repository is implemented on top of a database; this knowledge leaks through. If we wanted to implement the repository based on a web service, we might be able to repurpose the the ConnectionString property to a service URL, but it would be a hack at best - and how would we define security settings in that scenario?

Exposing a FileName property on an interface that represents an abstract resource is another example of a Leaky Abstraction.

Leaky Abstractions like these are often difficult to reuse. As an example, it would be difficult to implement a Composite out of the above IFooRepository - how do you aggregate a ConnectionString?

Conclusion

In short, using interfaces in no way guarantees that we operate with appropriate abstractions. Thus, the proliferation of interfaces that typically follow from TDD or use of DI may not be the pure goodness we tend to believe.

Creating good abstractions is difficult and requires skill. In a future post, I'll look at some principles that we can use as guides.


Comments

anonymous
How do you propose mocking/stubbing then?
2010-12-02 16:21 UTC
Good abstractions will still be interfaces (or base classes), so replacement with Test Doubles will still be possible.
2010-12-02 16:24 UTC
*they also say that an abstract class is preferred over an interface, I am guilty of the one interface per class, so can't wait to see your point of view for creating abstractions
2010-12-02 16:28 UTC
The whole discussion about abstract base classes versus interfaces is orthogonal to the point I'm trying to make, so I'm not going to go into that discussion in my next post.

The reason why the Framework Design Guidelines favor abstract classes is related to keeping options open for future extensions to abstractions without breaking backwards compatibility. It makes tons of sense when you just have to respect backwards compatibility when adding new features. This is the case for big, commercial frameworks like the BCL. However, I'm beginning to suspect that this is kind of a pseudo-argument; it's really more an excuse for creating abstractions that don't adhere to the Open/Closed Principle.

On the other hand, it isn't that important if you control the entire code base in question. This is often the case for enterprise applications, where essentially you only have one customer and at most a handful of deployments.

The problem with base classes is that they are a much more heavy-handed approach. Because you can only derive from a single base class, it becomes impossible to implement more than one Role Interface in the same class. Because of that constraint, I tend to prefer interfaces over base classes.
2010-12-02 17:57 UTC
So in order to change implementation of a dependency in c# (ie. Throw in a test double) i have to use an interface (unless i use TypeMock or the like).
The way I understand your post is not that you want me to change this, which is also impossible in a statically typed language.
Do I understand you correctly that you are more talking about how I design these dependencies so they would be - well better designed?
So in a way, if I understand you correctly, the things you are saying her could be applied in fx. Ruby as well, since they are design principles and not language stuff?
2010-12-02 20:53 UTC
Yes, the only thing I say is that an interface doesn't guarantee that you are using a good abstraction, but you can certainly use interfaces to model good abstractions - hence the Venn diagram.
2010-12-02 20:59 UTC
Daniel Gioulakis
Hi there Mark,
I've found this post to be quite an interesting read. At first, I was not in complete agreement, but as I took a step back and examined the way I write domain-driven code, I realized 99% of my code follows these concepts and rarely would I ever have a 1:1 relationship.

Today I happened upon a good video from one of Udi Dahan's talks: "Making Roles Explicit". It can be found here: http://www.infoq.com/presentations/Making-Roles-Explicit-Udi-Dahan

You can find some examples of his talk in practice here:
http://www.simonsegal.net/blog/2010/03/18/nfetchspec-code-entity-framework-repository-fetching-strategies-specifications-code-only-mapping-poco-and-making-roles-explicit/

If you happen to have some time to watch the speech, I was curious to hear your opinion on the subject as it seems like it would violate the concepts of this blog post. I still haven't wrapped my head around it all, but it would seem that Udi recommends creating interfaces for each "role" the domain object plays and using a Service Locator to find the concrete implementation ... or in his case the concrete FetchingStrategy used to pull data back from his ORM. This sounds like his application would have many 1:1 abstractions. I am familiar with your stance on the Service Locator and look forward to your book coming out. :)

Thanks,
Danny
2010-12-17 20:02 UTC
Hi Danny

Thanks for your comments. If you manage to read your way through my follow-up post you'll notice that I already discuss Role Interfaces there :)

I actually prefer Role Interfaces over Header Interfaces, but I can understand why you ask the questions you do. In fact, I went ahead and wrote a new blog post to answer them. HTH :)
2010-12-18 14:27 UTC
Emanuel Pasat
This blog post is asking for a quick real-time example (simple WCF or Azure example, let's say).
I assume you'll use same constructor injection with abstract factory but how will you avoid 1:1 mapping in these cases? Using the same trick with NullService and WcfService?
What is a good abstraction for a Wcf service or for a CloudQueueClient?

Thanks in advance
2010-12-24 11:23 UTC
I recently wrote an Azure application where I used these abstractions on top of Azure Queues:

public interface IChannel { void Send(object message); }

for sending messages, and this one to handle them:

public interface IMessageConsumer<T> { void Consume(T message); }

Since both are Commands (they return void) they compose excellently.
2010-12-28 08:44 UTC
Emanuel Pasat
Quote from RAP (http://parlezuml.com/blog/?postid=934) :

"If the only class that ever implements the Customer interface is CustomerImpl, you don't really have polymorphism and substitutability because there is nothing in practice to substitute at runtime. It's fake generality. All you have is indirection and code clutter, which just makes the code harder to understand."

From there I thought that 1:1 mapping means one implementation to one interface.
From your example it seems that 1:1 mapping referes to not having all members of an implementation matching exactly all members of the interface.

Which one is true?
Thanks again
2010-12-29 08:14 UTC
No, I meant exactly the same as the RAP post. Not implementing all members would indicate that that an interface has more than one members. That would smell of a Header Interface, and I prefer Role Interface.

What gave you that other impression?
2010-12-29 12:12 UTC
Emanuel Pasat
The fact that IChannel should have more implementations.

But that's usually accomplished with decorators (we always need cross cutting concerns) and null objects, right?
2010-12-29 12:43 UTC
Yes, that's right, but a Decorator is also an implementation of an interface. As soon as you define your first Decorator, by implication you already have two implementations of the same type.

With an interface like IChannel, a Composite also becomes possible, in the case that you would would like to broadcast a message on multiple channels.
2010-12-29 13:13 UTC
Simple
Can you show please some examples where you use some kind of Test Doubles for Unit Tests without 1:1 Interface implementation? ))

Thanks )




2012-04-21 12:57 UTC
For a framework example, see AutoFixture, which contains some 3000 unit tests - many of them with Test Doubles. Most (if not all) of the interfaces have several implementations, e.g. ISpecimenBuilder.

For a more complete application, see the Booking sample CQRS application. It uses Moq for Test Doubles, and I very consciously wrote that code base with the RAP in mind.
2012-04-22 06:09 UTC
Simple
Maybe Ill better show some of my code later and ask - where the problem is =)


Do you have plans to publish some books about software development? Some kind of patterns explanation.. or best practices for .NET developers - with actual technologies? (not only DI =))

I think you can not only develop something but also explain how =)

2012-04-23 17:42 UTC
Currently, I don't have any concrete plans for new books, but it's not unlikely that I'll write another book in the future.
2012-04-23 18:12 UTC


Wish to comment?

You can add a comment to this post by sending me a pull request. Alternatively, you can discuss this post on Twitter or Google Plus, or somewhere else with a permalink. Ping me with the link, and I may add it as a comment.

Published

Thursday, 02 December 2010 13:03:04 UTC

Tags



"Our team wholeheartedly endorses Mark. His expert service provides tremendous value."
Hire me!