Interfaces vs Abstract Classes as a module boundary.

Started by
16 comments, last by dilyan_rusev 11 years, 9 months ago
Yet another debate at work where I am interested in hearing a wider variety of opinions:

When designing a module boundary for C# code and you need to define 'what that module needs to provide so that mine may work', when/how/why should an interface be used and when/how/why should an abstract base class be used?


Again, I will withhold my opinions until there's been sufficient response as to not cause bias.

Advertisement
They are equivalent except for the fact that C# does not support multiple inheritance. That means while you can implement multiple interfaces, you cannot have more than one base class, abstract or otherwise. If it truly is an abstract base class (no functionality built in at all), I always use interfaces so not to preclude the use of a base class, and question the usefulness of a pure abstract base class.
My vote goes to interface, becouse it provides only the public interface, a contract for all implementations which better not throw any NotImplementedException crap.

Abstract classes may have methods for base functionality, and it's considered as code reuse by inheritance, which I'm usually against of. Base functionality provides only clutter, implementation details, and you can't be sure that the derived class actually calls the base methods.

When adding new methods, some say abstract classes are better, becouse you can add new methods without breaking (modifying) derived classes. I say it is good that compiler points every class implementing an interface which needs to be modified.
I wouldn't vote for either interface or abstract class per-se.

In case the module boundary is simply a contract that defines the required properties and operations, interfaces are very suitable, espacially due to the single inheritance restriction mentioned tstrimple. I would argue against the notion that adding methods or properties breaks existing code when using interfaces, though. The safe way of doing so would be to actually define a derived interface.

Personally, I prefer interfaces with some kind of (optionally) abstract default implementation added to the mix. I don't consider base functionality to be "clutter", since - provided said functionality is carefully chosen - it greatly helps with code reuse and avoids code duplication,

In summary I prefer interfaces to define the basic contracts along with (abstract) base classes that provide a basic implementation. That way not a single style is forced upon developers. I regard "pure" abstract classes rather useless, though, in contrast to empty "marker" interfaces, which can be useful and don't hurt anyone except some pedantic "code quality tools".

Personally, I prefer interfaces with some kind of (optionally) abstract default implementation added to the mix. I don't consider base functionality to be "clutter", since - provided said functionality is carefully chosen - it greatly helps with code reuse and avoids code duplication,

In summary I prefer interfaces to define the basic contracts along with (abstract) base classes that provide a basic implementation.

I'd argue that the shared base implementation does not need to be part of the interface, nor class hierarchy, but can be moved to a helper class which is composited in the actual implementation. And the code can be shared, and IMO allows faster response to future changes. The only downside is that there's some boilerplate for each class to forward the calls to the base implementation.

Personally, I prefer interfaces with some kind of (optionally) abstract default implementation added to the mix. I don't consider base functionality to be "clutter", since - provided said functionality is carefully chosen - it greatly helps with code reuse and avoids code duplication,


I have used this successfully for WCF services. There were multiple interfaces which describe a service, and some have common functionality, authorization for instance. The service interface "implemented" the auth interface, and the service that implemented the contract also inherited from an auth class which provided the authentication functionality. Something similar to the following:

public interface IAuthorization
{
AuthToken CreateAuthToken(Credentials credentials);
bool IsValidAuthToken(AuthToken token);
void DestroyAuthToken(AuthToken token);
}
public AuthProvider : IAuthorization
{
// Implement IAuthorization
}
public interface INewService : IAuthorization
{
bool CreateUser(AuthToken token, User user);
}
public NewService : IService, AuthProvider
{
// Implements INewService
}
Okay, there's been enough time I think.

I tend to fall more on the abstract base class side of the fence than my coworkers. In general, I will use an interface if I am specifying some trait that things with different purposes can provide, and a abstract class if I am defining something that must be supplied. Hopefully that's mostly clear.

The main problem from my perspective is that the multiple inheritance benefit that you guys talk about is not something that is always benefit. Sure, sometimes it is. But I don't like using them when I am defining some responsibility that needs to be supplied by the module downstream. It practically invites people to violate SRP. "I need to supply that module with this responsibility; oh, it's an interface, I can just mush these things together to satisfy it!". And you get a mess.

Or people extend that interface with another. More often than not, the source module then needs to do type checking/casting to see if some other interface happens to be on the returned type, violating LSP.

And really, in many cases (though certainly not all) if you have a few different implementations of some responsibility, odds are they are going to have common state/behavior. If you represent that as an interface, you either get to have some base class above the interface (which tends to [but not always] cause a bit of duplicate work since the interface and base class share declarations) or violate DRY via simply duplicating implementations.

...and they're so annoying to work with when editing/reading a sizeable codebase. Go to Definition is a dead end, and it's (usually) harder to identify what can be the implementation at a given point.


Now I'll use interfaces plenty. When the module separation is WCF, then interfaces make sense. When the implementations will be really varied (and thus unlikely to share common behaviors). When the thing I'm modeling is a trait rather than a responsibility. But when doing this sort of thing I'll often default to an abstract base class and only then restrict the code to the multiple inheritance limitations if I need that feature. It seems as though my coworkers default to interfaces and then go straight to concrete classes (or more interfaces + type checking) if any commonalities are found or requirements change/expand.

It drives me mildly nuts seeing how this sort of thing has gotten them into loads of trouble, but I cannot convince them that interface overuse is the root cause.
Interfaces are the only way to responsibly evolve your API (in case you are selling components/engines). Now, they aren't as useful as in Java with its inner anonymous classes, and casting is a pain, but still I do prefer interfaces.

One problem that you might run into (that we did at my previous job) is Interface Evolution Done the Wrong Way. That means when you add new bits, you extend the interface (instead of providing a new one, that does not necessarily inherit the old one). In two years the interfaces clients were supposed to implement contained 20+ methods, plus it depended on the presence of some static factory methods in your implementation. Quite ugly (I shudder a little every time I think of the code base).

When you extend an interface, you might provide some sort of service locator pattern. Something like COM's QueryInterface, which is built right into the first version. Now, if the extension does not inherit the original interface, you can have a lot of flexibility in the client, providing them with the ability to swap the implementation if the locator had some context, letting either the client or the library to pick an implementation.

Abstract classes are useful for skeleton implementations. But it takes a lot of discipline to evolve it properly. You have to implement every tiny little bit of the implementation details, so that they know what and how to extend. And since in C# methods are not virtual by default, you have to make some very important decisions as to what can be overridden. If you upgrade your abstract class to include a new method, it must not be abstract, or it will break all clients. Seems impossible to do it, but I've seen it happen a few times, and most of my colleagues were quite good (but time pressure and deadlines can slip in something like that).

Another way to extensibility is through events. I've seen it at other products my ex-company did. Basically at key extension points events are raised, and you can configure the object. That lets you change the internals any way you want, so long as you keep raising and respecting the events. It is somewhat less flexible in terms of what you can do, but it lends itself very well to designers, since code generation is extremely simple (you don't have to keep track of what is implemented. This is component C and event E => all you need to do is insert a default handler).

Finally, you can extend with templates or some XML (essentially data-driven). Done that, but providing default templates, and maintaining template/data compatibility across versions (especially default values and the fact that templates may reside in DB, so a schema change is necessary) is as much a pain as maintaining interfaces/abstract classes.

And as to abstract classes vs interfaces... that depends on the code base. If am starting a new one, I'd rather provide an interface. I'd do the event thing whenever possible because of the great freedom it provides.
Just to add that interfaces are generally easier to mock and hence unit test client code.


...and they're so annoying to work with when editing/reading a sizeable codebase. Go to Definition is a dead end, and it's (usually) harder to identify what can be the implementation at a given point.

Resharper. Ctrl + F12. Go To Implementation.

I would have paid $150 for that feature on it's own :D
if you think programming is like sex, you probably haven't done much of either.-------------- - capn_midnight

Just to add that interfaces are generally easier to mock and hence unit test client code.


Care to elaborate on this? I don't see how they're really any easier to mock.

This topic is closed to new replies.

Advertisement