Interfaces and abstract classes in .NET are pretty similar in
functionality - you can't instantiate them, but still objects of
various runtime types can hide both behind an interface or behind an
abstract class. So when to use which?
The "Framework Design Guidelines" book by Brad Abrams and Krzysztof Cwalina
is a very good book for all those who like asking such questions.
Particularly, there is a whole section 4.3 "Choosing between class and
interface" discussing this issue. I'll spoil the plot ending and post
the main advice right away:
Do favor defining classes over interfaces.
main argument of the authors is flexibility of classes. This applies
mostly to layered architectures, where a client layer uses the
framework layer. Classes can provide default implementation, so even if
you already have derived classes in your client layer and you're adding
a new virtual method to your base class in the framework, the clients
won't even have to recompile their derived classes - the new virtual
method magically appears in the derived types automatically and you
don't have to change anything in the client. On the contrary, if you
add a new method to an interface, which is already being implemented by
some clients, you can't provide the default implementation - it would
introduce a breaking change (all clients would have to manually add
methods to implement the new method of the interface).
This is a
very good point - once you ship an interface you basically can't change
it anymore - it's carved in stone starting from the very moment anyone
on the client side implements it. Unless you want to break that client
and make them recompile or even worse - re-deploy.
When I was
developing an editor for C# it was based on a framework for editors.
During design of the framework and the editor, I learned a lot about
how to use interfaces (more precisely, how not to use them).
Now I want to share my experiences with you to add a couple of measly
cents to the dollars of wisdom of .NET architects.
editor framework is built around a concept of a 'block' - a rectangular
thing which can be displayed on the screen and contain text and other
blocks. The framework is basically a hierarchy of classes that inherit
from the Block class. If you're familiar with the Composite design pattern and the text editor example from the GoF book, you'll immediately recognize that blocks are simply Glyphs - basic elements from which documents are composed. Now, I had an interface IBlock and an abstract class Block, which implemented IBlock. As it turned out many lines of code later, this was the most stupid thing to do:
- Anytime I wanted to add new members to the class hierarchy, I had to update both the base class and the interface
- Once I'd ship the IBlock interface and a client would implement it, I wouldn't be able to add anything to it anymore
- The only situation where IBlock would be useful is when some client would want to implement it without inheriting from Block - and who on earth would ever want that?
I removed the IBlock interface and changed all its usages to use the abstract class Block instead. This simplified my life so much.
What I've learned from this: interfaces are good when you want to work with some distinguished feature or ability
of an entity, and not the whole entity. For example, if you want to
enumerate something, it is sufficient for you to receive an IEnumerable
- you don't care about the rest of the entity. It can be anything, of
any complexity - for you is only important that it allows to enumerate
itself. A good occasion to use an interface is having many base classes
- you don't have to inherit from Collection, you have various base
classes and the only thing you have to do is to implement IEnumerable.
As I said, interfaces describe part of the functionality, and you can mix-in this functionality to existing class hierarchies without adding a new base class.
on the opposite, are good for modeling whole entities, entire complete
entities as a whole. That was my mistake - I used an interface for the
whole entity "Block", where I should have used a class instead. With
classes, you can't mix in a new base class to an existing one - that
would mean merging two clearly separate stand-alone entities into some
weird-looking hybrid. That is one of the reason multiple inheritance is
not allowed in .NET - you shouldn't mix entities together, it's not a
good practice. On the opposite, you should do your best to separate
your entities from one another. It's like normalization of relational
databases - you split your tables until each table describes a clearly
defined entity with exact boundaries and strictly defined connections
and relationships to other entities.
A common design mistake is
called "burning the base class" - it's when you make your whole
hierarchy inherit from some base class just to add some functionality
to the entire class hierarchy in one simple step. As a rule, this
functionality doesn't represent a separate, stand-alone entity, but
still you waste your base class to add partial functionality. As I said
before, interfaces are best when you want to add part of a
functionality - you can mix it in to an existing hierarchy without
burning the base class. An example of burning the base class is a
System.MarshalByRefObject from the .NET base class library. Clearly, we
waste a base class of all Windows Forms controls just to add some ability
to all the classes at once. Adding an ability is done best by
interfaces. However, this way requires more typing because an interface
can't provide a default implementation.
To reiterate: use
classes to model whole entities, and use interfaces to add abilities to
existing entities. The only exception to this rule I can currently
think of - is when you don't want your entities to be extended in the
future. That is, you'd like to specify a precise contract for an
entity, and you can guarantee that this entity won't have additional
abilities in the future - at least not in your scope. Then, it's OK to
use an interface to capture the entity and carve it in stone. Clients
are free to add functionality to this entity, but you don't want to see
it in your framework. You'll only work with this strictly defined
entity and it's not going to change.
Finally, I've discovered a rule of thumb for myself which generally helps me to use interfaces correctly. An interface should define at most one member. Yes, that easy. One could even have an FxCop rule for it. If you want to define an ability
on some entity, one member is mostly enough: GetEnumerator() for an
enumerable, Clear() for a clearable, Count for countable and so on and
so forth. If you want a more complex ability or a composed entity, use
ICollection : IAllowsAdd, IAllowsRemove, IEnumerable, IClearable, Indexable, ICountable, ISet
Purists can push this rule even further:
An interface can either:
- declare no members and inherit from no interfaces ("a marker interface"), or
- declare a single member, or
- inherit from two other interfaces.
This would be some minimal "interface normal form" à la Chomsky,
to which any interface hierachy could be reduced. Unfortunately, this
mathematically beautiful "normal form" wouldn't be practical for
Post a Comment