Naming of Interfaces and Implementations
Many of you are probably familiar with code containing an interface
FormDataValue and implementation classes
FormDataValueImpl. However I received consent on my complacent criticism, accompanied by an excuse like: “Our team decided not to follow a naming condition like e.g.
I feel my criticism was misunderstood? There are almost always more than two options in naming classes. Why do some teams only find these two? I will try to analyze the problem more deeply …
Which Problem is solved with Single Implementation Interfaces?
The answer is not trivial. In direct Conversations I often felt, that there are two objectives:
- decoupling an abstract interface from the concrete implementation
- even in the long run there will only be a single implementation
The most popular purpose of decoupling is that code gets more flexible to changes in future. But constraining the implementations to exactly one is contradictory (we explicitly constrained ourselves not to be flexible). Actually the idea of having exactly one implementation often leads to a programming style, where this constraint is also exploited in code, e.g. by casting to the implementation class, e.g..
FormDataValue value = ...; // other code FormDataValueImpl impl = (FormDataValueImpl) value; impl.doNonInterfaceAction();
From my point of view one should critically consider, whether it is more important to be decoupled or to constrain yourself to exaclty one implementation: These objectives are contradictory:
- whoever uses decoupling as explicit desgign principle (e.g. by definining new implementation classes) will eventually have multiple implementations
- whoever uses the constraint of a single implementation (e.g. by casting from interface to implementation class) will eventually violate decoupling
Why …Impl and I… are not good naming
In general we expect names to be precise and descriptive for the described object. With
I... we do not describe the object, but the role in our programming language, to be specific, whether the object is an interface or an implementation, both information
- that is redundant at definition time (we can clearly see what it is)
- and that should not change anything at use time (we should not care about what it is)
For the name of an implementation we should be careful that the way of this implementation it is described in some way. We will have to face the following problem of .NET collections, if we disrespect this principle:
For an interface
IList we find a sub type
List, but from the name nobody will know what to expect from this list. Java and .NET developers will probably expect to find an array based list, functional developers (using Haskell or ML) tend to think that linked lists should be the default. I can only speculate about the thoughts of F# developers (that are functional and .NET). Many third party implementations will have to decide whether to comply to this convention (and consequently call their own array lists
List, which would lead to ambiguities) or to break with this convention (which also confuses the user). The collection library C5 breaks the convention and calls its array-based list
In Java we find and interface
List with implementations
LinkedList. This is more robust and if we have a view on third party collection libraries we will find many collection names, that are descriptive and consistent. The collection library fastutil provides different implementations for object and primitive types so the names are even more specific then in the java api (e.g.
However there are good reasons not to describe the implementation in the type name, especially if later changes (of the current implementation) are expected. In this case we can assign a prefix like
Maybe some of the readers might argue that
DefaultFormDataValue is not better than
FormDataValueImpl. After all there are following arguments:
Defaultexpresses the intended role. The type is the default implementation, which should be used in case of doubt.
Impldoes not express the same, we only assume the same because of conventions.
Defaultis linguistically more correct. What
FormDataValue(that should be used as default).
FormDataValueImpl). Whoever reads the implementation inheritances as
is awill read
DefaultFormDataValue is FormDataValue(which is sound) and
FormDataValueImpl is a FormDataValue(which cannot be derived linguistically).
Defaultis correct because pure logic implies that there is only one default. On the other hand
Implimplies that there is an implementation (but there could be more …).
Defaultis more robust for future implementations. If we extend our
Implcode by a compact implementation, we would have
FormDataValueCompactImpl(where linguistically the second is a subconcept of the first). With
Defaultwe would have
CompactFormDataValue(both are linguistically distinguishable)
Defaultreminds the user that there may be non default implementations. Everyone directly casting the interface into the implementation type cannot argue that he skipped an explicit type check because there cannot be more implementations.
- Maybe an old implemention should be replaced (e.g.
CompactDataValueget the new default). Renaming
DataValueImpllets the user riddling about the name changed.
...Impl introduce a fragile naming convention:
I...only works if the whole community enforces this convention (this is probably only the case for .NET)
...Implbreaks immediately, if somebody plans multiple implementations for an interface. In this case the developer must decide, whether to use one
Impl(with other implementations deviating from the convention) or to abandon the convention completely. There are no community best practices to solve this conflict in a consistent way.
Some more detailed and other aspects can be found here (1,2,3).