70

Uncle Bob's chapter on names in Clean Code recommends that you avoid encodings in names, mainly regarding Hungarian notation. He also specifically mentions removing the I prefix from interfaces, but doesn't show examples of this.

Let's assume the following:

  • Interface usage is mainly to achieve testability through dependency injection
  • In many cases, this leads to having a single interface with a single implementer

So, for example, what should these two be named? Parser and ConcreteParser? Parser and ParserImplementation?

public interface IParser {
    string Parse(string content);
    string Parse(FileInfo path);
}

public class Parser : IParser {
     // Implementations
}

Or should I ignore this suggestion in single-implementation cases like this?

8 Answers8

188

Whilst many, including "Uncle Bob", advise not to use I as a prefix for interfaces, doing so is a well-established tradition with C#. In general terms, it should be avoided. But if you are writing C#, you really should follow that language's conventions and use it. Not doing so will cause huge confusion with anyone else familiar with C# who tries to read your code.

Bryan Oakley
  • 25,479
David Arno
  • 39,599
  • 9
  • 94
  • 129
41

The interface is the important logical concept, hence, the interface should carry the generic name. So, I'd rather have

interface Something
class DefaultSomething : Something
class MockSomething : Something

than

interface ISomething
class Something : ISomething
class MockSomething : ISomething

The latter has several isues:

  1. Something is only one implementation of ISomething, yet it is the one with the generic name, as if it was somehow special.
  2. MockSomething seems to derive from Something, yet implements ISomething. Maybe it should be named MockISomething, but I've never seen that in the wild.
  3. Refactoring is harder. If Something is a class now, and you only find out later that you have to introduce an interface, that interface should be named the same, because it should be transparent to clients whether the type is a concrete class, an abstract class or an interface. ISomething breaks that.
wallenborn
  • 1,980
29

This isn't just about naming conventions. C# doesn't support multiple inheritance so this legacy use of Hungarian notation has a small albeit useful benefit where you're inheriting from a base class and implementing one or more interfaces. So this...

class Foo : BarB, IBarA
{
    // Better...
}

...is preferable to this...

class Foo : BarB, BarA
{
    // Dafuq?
}

IMO

Robbie Dee
  • 9,823
18

No no no.

Naming conventions are not a function of your Uncle Bob fandom. They are not a function of the language, c# or otherwise.

They are a function of your code base. To a much lesser extent of your shop. In other words the first one in the code base sets the standard.

Only then do you get to decide. After that just be consistent. If someone starts being inconsistent take away their nerf gun and make them update documentation until they repent.

When reading a new code base if I never see an I prefix I'm fine, regardless of language. If I see one on every interface I'm fine. If I sometimes see one, someone's going to pay.

If you find yourself in the rarefied context of setting this precedent for your code base I urge you to consider this:

As a client class I reserve the right to not give a damn what I'm talking to. All I need is a name and a list of things I can call against that name. Those may not change unless I say so. I OWN that part. What I'm talking to, interface or not, is not my department.

If you sneak an I in that name, the client doesn't care. If there is no I , the client doesn't care. What it's talking to might be concrete. It might not. Since it doesn't call new on it either way it makes no difference. As the client, I don't know. I don't want to know.

Now as a code monkey looking at that code, even in java, this might be important. After all, if I have to change an implementation to an interface I can do that without telling the client. If there is no I tradition I might not even rename it. Which might be an issue because now we have to recompile the client because even though the source doesn't care the binary does. Something easy to miss if you never changed the name.

Maybe that's a non issue for you. Maybe not. If it is, that's a good reason to care. But for the love of desk toys don't claim we should just blindly do this because it's the way it's done in some langauge. Either have a damn good reason or don't care.

It's not about living with it forever. It's about not giving me crap about the code base not conforming to your particular mentality unless you're prepared to fix the 'problem' everywhere. Unless you have a good reason I shouldn't take the path of least resistance to uniformity, don't come to me preaching conformity. I have better things to do.

candied_orange
  • 119,268
8

There's one tiny difference between Java and C# that is relevant here. In Java, every member is virtual by default. In C#, every member is sealed by default - except for interface members.

The assumptions that go with this influence the guideline - in Java, every public type should be considered non-final, in accordance with Liskov' Substitution Principle [1]. If you only have one implementation, you'll name the class Parser; if you find that you need multiple implementations, you'll just change the class to an interface with the same name, and rename the concrete implementation to something descriptive.

In C#, the main assumption is that when you get a class (name doesn't start with I), that's the class you want. Mind you, this is nowhere near 100% accurate - a typical counter-example would be classes like Stream (which really should have been an interface, or a couple of interfaces), and everyone has their own guidelines and backgrounds from other languages. There's also other exceptions like the fairly widely used Base suffix to denote an abstract class - just like with an interface, you know the type is supposed to be polymorphic.

There's also a nice usability feature in leaving the non-I-prefixed name for functionality that relates to that interface without having to resort to making the interface an abstract class (which would hurt due to the lack of class multiple-inheritance in C#). This was popularised by LINQ, which uses IEnumerable<T> as the interface, and Enumerable as a repository of methods that apply to that interface. This is unnecessary in Java, where interfaces can contain method implementations as well.

Ultimately, the I prefix is widely used in the C# world, and by extension, the .NET world (since by far most of .NET code is written in C#, it makes sense to follow C# guidelines for most of the public interfaces). This means you will almost certainly be working with libraries and code that follows this notation, and it makes sense to adopt the tradition to prevent unnecessary confusion - it's not like omitting the prefix will make your code any better :)

I assume that Uncle Bob's reasoning was something like this:

IBanana is the abstract notion of banana. If there can be any implementing class that would have no better name than Banana, the abstraction is entirely meaningless, and you should drop the interface and just use a class. If there is a better name (say, LongBanana or AppleBanana), there's no reason not to use Banana as the name of the interface. Therefore, using the I prefix signifies that you have a useless abstraction, which makes the code harder to understand with no benefit. And since strict OOP will have you always code against interfaces, the only place where you wouldn't see the I prefix on a type would be on a constructor - quite pointless noise.

If you apply this to your sample IParser interface, you can clearly see that the abstraction is entirely in the "meaningless" territory. Either there's something specific about a concrete implementation of a parser (e.g. JsonParser, XmlParser, ...), or you should just use a class. There's no such thing as "default implementation" (though in some environments, this does indeed make sense - notably, COM), either there's a specific implementation, or you want an abstract class or extension methods for the "defaults". However, in C#, unless your codebase already omits the I-prefix, keep it. Just make a mental note everytime you see code like class Something: ISomething - it means somebody isn't very good at following YAGNI and building reasonable abstractions.

[1] - Technically, this isn't specifically mentioned in Liskov's paper, but it is one of the foundations of the original OOP paper and in my reading of Liskov, she didn't challenge this. In a less strict interpretation (the one taken by most OOP languages), this means that any code using a public type that is intended for substitution (i.e. non-final/sealed) must work with any conforming implementation of that type.

Luaan
  • 1,860
3

So, for example, what should these two be named? Parser and ConcreteParser? Parser and ParserImplementation?

In an ideal world, you should not prefix your name with a short-hand ("I...") but simply let the name express a concept.

I read somewhere (and cannot source it) the opinion that this ISomething convention leads you to define an "IDollar" concept when you need a "Dollar" class, but the correct solution would be a concept called simply "Currency".

In other words, the convention gives an easy out to the difficulty of naming things and the easy out is subtly wrong.


In the real world, the clients of your code (which may just be "you from the future") need to be able to read it later and be familiar with it as fast (or with as little effort) as possible (give the clients of your code what they expect to get).

The ISomething convention, introduces cruft/bad names. That said, the cruft is not real cruft *), unless the conventions and practices used in writing the code are no longer actual; if the convention says "use ISomething", then it is best to use it, to conform (and work better) with other devs.


*) I can get away with saying this becasuse "cruft" is not an exact concept anyway :)

utnapistim
  • 5,313
2

As the other answers mention, prefixing your interface names with I is part of the coding guidelines for the .NET framework. Because concrete classes are more often interacted with than generic interfaces in C# and VB.NET, they are first-class in naming schemes and thus should have the simplest names - hence, IParser for the interface and Parser for the default implementation.

But in the case of Java and similar languages, where interfaces are preferred instead of concrete classes, Uncle Bob is right in that the I should be removed and interfaces should have the simplest names - Parser for your parser interface, for instance. But instead of a role-based name like ParserDefault, the class should have a name that describes how the parser is implemented:

public interface Parser{
}

public class RegexParser implements Parser {
    // parses using regular expressions
}

public class RecursiveParser implements Parser {
    // parses using a recursive call structure
}

public class MockParser implements Parser {
    // BSes the parsing methods so you can get your tests working
}

This is, for instance, how Set<T>, HashSet<T>, and TreeSet<T> are named.

-8

You are correct in that this I = interface convention breaks the (new) rules

In the old days you would prefix everything with its type intCounter boolFlag etc.

If you want to name things without the prefix, but also avoid 'ConcreteParser' you could use namespaces

namespace myapp.Interfaces
{
    public interface Parser {...
}


namespace myapp.Parsers
{
    public class Parser : myapp.Interfaces.Parser {...
}
Ewan
  • 83,178