Jump to content

  • Log In with Google      Sign In   
  • Create Account

Interfaces vs Abstract Classes as a module boundary.


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
17 replies to this topic

#1 Telastyn   Crossbones+   -  Reputation: 3730

Like
0Likes
Like

Posted 20 July 2012 - 01:03 PM

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.

Sponsor:

#2 tstrimple   Prime Members   -  Reputation: 1734

Like
3Likes
Like

Posted 20 July 2012 - 01:15 PM

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.

Edited by tstrimple, 20 July 2012 - 01:17 PM.


#3 Codarki   Members   -  Reputation: 462

Like
0Likes
Like

Posted 20 July 2012 - 01:21 PM

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.

#4 darookie   Members   -  Reputation: 1437

Like
1Likes
Like

Posted 20 July 2012 - 01:55 PM

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".

#5 Codarki   Members   -  Reputation: 462

Like
1Likes
Like

Posted 20 July 2012 - 02:29 PM

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.

#6 tstrimple   Prime Members   -  Reputation: 1734

Like
0Likes
Like

Posted 20 July 2012 - 02:46 PM

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
}


#7 Telastyn   Crossbones+   -  Reputation: 3730

Like
1Likes
Like

Posted 21 July 2012 - 04:02 PM

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.

#8 dilyan_rusev   Members   -  Reputation: 1068

Like
0Likes
Like

Posted 22 July 2012 - 11:40 AM

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.

#9 ChaosEngine   Crossbones+   -  Reputation: 2499

Like
0Likes
Like

Posted 22 July 2012 - 04:21 PM

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

#10 Telastyn   Crossbones+   -  Reputation: 3730

Like
1Likes
Like

Posted 22 July 2012 - 06:03 PM

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.

#11 ChaosEngine   Crossbones+   -  Reputation: 2499

Like
0Likes
Like

Posted 22 July 2012 - 09:52 PM


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.


Sure. Mocking frameworks such as NMock, Rhino Mocks and Moq mostly work with interfaces. Some of them have some support for mocking abstract classes, but it's a lot easier to use interfaces (hence my comment).


IMO, both have their place. From the perspective of a consumer of a component/class/service, an interface makes the most sense. For an implementer of a service, if there is shared state or functionality, then an abstract class will, as you pointed out reduce DRY.

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.


Ughhh. I agree that is ugly, but I'd argue that's down to bad design. A client should be given the bare minimum of what it needs to do it's work and no more.


My main complaint regarding interfaces is the inability to specify constraints on construction (e.g. an implementing class must supply a constructor with these parameters).
For example, if I have a generic Presenter class that manages a View, it would be nice to be able to write code like this

[source lang="csharp"]TPresenter<TView> BuildPresenter(TView view) where TView : IView where TPresenter<TView> : IPresenter<TView>, new(TView) // doesn't exist in C#{ var presenter = new TPresenter<TView>(view); // do some work with presenter return presenter;}[/source]


You can already specify a parameter-less constraint, I don't see why that couldn't have been extended. Would save creating factory classes.

Edited by ChaosEngine, 22 July 2012 - 09:55 PM.

if you think programming is like sex, you probably haven't done much of either.-------------- - capn_midnight

#12 Telastyn   Crossbones+   -  Reputation: 3730

Like
0Likes
Like

Posted 23 July 2012 - 05:06 AM

Sure, that makes sense. I don't remember having problems with Moq or Rhino Mock, but then again I tend to avoid those sort of frameworks when I can.

I agree that it is bad design for the typecasting, though it is pervasive in this new code base I am working with. Part of me sees the pervasive use of manual type checks and casting, the duplication of interface implementation, and the non-existent use of abstract classes... And can't help to make a correlation.

#13 dilyan_rusev   Members   -  Reputation: 1068

Like
0Likes
Like

Posted 23 July 2012 - 06:26 AM

About unit testing... that is not quite true. It is absolutely the same, except for methods that are not virtual. This is easily fixable through the profiler API, though it requires some C++/COM. The mocking frameworks you listed are amateur grade, but if you pick a more advanced tool, you will be able to mock static & sealed classes, static methods & stuff. The problem there is execution speed, as dynamically rewriting the method body/class definition isn't the fastest thing to do, as all optimizations go to hell (plus you do use some sort of a profiler).

Like I said, casting is easily fixable with even basic utilization of the locator pattern - you just look up for features. Of course, you would use a generic interface to have no casting. If the client wants the feature, you just do your stuff. Much more civilized than simple casting.

In my opinion, using only interfaces or only abstract classes is like fanaticism. Abstract classes are very useful when you have classes with a lot of methods (e.g. a class for CRUD operations on more than one type). You really don't want to make the client implement 20 or so methods in an interface.

About restrictions on creation - I disagree. I've grown to appreciate the idea that constructors are just for initializing your fields. You throw less exceptions, your objects are always in valid state (except for statics in parallel code), they are easy to unit test, and code is simpler overall. Why add factory for something as simple as allocating memory? If you use IoC, you already have free factories for the interfaces that you provide. For interfaces that the clients must provide/implement, object creation is an implementation detail, and I would not put any restrictions on it.

#14 ChaosEngine   Crossbones+   -  Reputation: 2499

Like
0Likes
Like

Posted 23 July 2012 - 03:41 PM

The mocking frameworks you listed are amateur grade, but if you pick a more advanced tool, you will be able to mock static & sealed classes, static methods & stuff.


Can you give me an example of a "more advanced tool"? I agree that NMock isn't great, but I've found Rhino to be pretty good.


Like I said, casting is easily fixable with even basic utilization of the locator pattern - you just look up for features. Of course, you would use a generic interface to have no casting. If the client wants the feature, you just do your stuff. Much more civilized than simple casting.


Sweet monkey jesus, no. That's dragging us back to the nightmare of COM and QueryInterface. It's needless complexity and an anti-pattern. Give the client what it needs to do it's work.

In my opinion, using only interfaces or only abstract classes is like fanaticism. Abstract classes are very useful when you have classes with a lot of methods (e.g. a class for CRUD operations on more than one type). You really don't want to make the client implement 20 or so methods in an interface.


Yep, as I said, they were designed for different purposes. Interfaces are designed to expose a contract to a client, abstract classes are designed to share implementation.
Although a abstract class with 20 methods is a code smell.

About restrictions on creation - I disagree. I've grown to appreciate the idea that constructors are just for initializing your fields. You throw less exceptions, your objects are always in valid state (except for statics in parallel code), they are easy to unit test, and code is simpler overall. Why add factory for something as simple as allocating memory? If you use IoC, you already have free factories for the interfaces that you provide. For interfaces that the clients must provide/implement, object creation is an implementation detail, and I would not put any restrictions on it.


Fair point.
if you think programming is like sex, you probably haven't done much of either.-------------- - capn_midnight

#15 dilyan_rusev   Members   -  Reputation: 1068

Like
0Likes
Like

Posted 23 July 2012 - 06:02 PM

Can you give me an example of a "more advanced tool"? I agree that NMock isn't great, but I've found Rhino to be pretty good.


Typemock and JustMock come on the top of my mind. If you don't need sealed/non-virtual/static mocking, Rhino is a good enough framework. It's not like it is impossible to mock statics and .NET types, but that requires wrapping and additional work on your part (e.g. utility methods that call the unmockable static method and then mock that). Rhino works by subclassing, so you are in trouble if you need to mock private stuff (if memory serves correctly). That way the framework forces you to make the access level to protected or internal, which sucks a great deal. You might argue that you should not mock & unit test private methods. And you will be right. Yet there are some legitimate cases for that (where you want to hide implementation details in utility methods), but overall it is a bad idea. Nevertheless, I don't like the mocking framework to dictate higher visibility than what is reasonably required.

Sweet monkey jesus, no. That's dragging us back to the nightmare of COM and QueryInterface. It's needless complexity and an anti-pattern. Give the client what it needs to do it's work.


Casting is the same stuff as QueryInterface. I don't see the functional difference between
var sth = baseInstance.GetExtension<IExtension>();
if (sth != null)
{
	 this.DoExtension(sth);
}
and
var sth = baseInstance as IExtension;
if (sth != null)
{
	 this.DoExtension(sth);
}
... except that the first gives you the flexibility to return an instance that is different than baseInstance. Why not implement additional features in smaller classes, if it makes sense? If not, just return this and be done with it. I guess it is not necessary for the majority of what people need, but that doesn't mean the idea is bad and rotten to the core. If you don't need that flexibility, don't use it - it will make the codebase better - just like with everything else in programming. But it will also simply stuff if you need it.

The glorified IoC frameworks like Unity & Structure Map use the same concept. When applied properly, it is not a bad thing. Maybe I haven't worked that much with COM to dislike it. It is a technology that made sense at the time for what it was intended to do: C++ binary compatibility for modules (like what C is, and what WinRT will do with supported languages).

#16 Telastyn   Crossbones+   -  Reputation: 3730

Like
0Likes
Like

Posted 23 July 2012 - 07:23 PM

Why not implement additional features in smaller classes, if it makes sense?


Because you end up doing this sort of casting/QueryInterface, which is fragile at best.

You end up knowing too much about the implementation details of subclasses, violating LSP or the Law of Demeter depending on your viewpoint.

I'm not saying that things shouldn't end up in smaller classes (quite the opposite), but those smaller classes should be composed rather than exposed via a query interface or conditional casting.

The glorified IoC frameworks like Unity & Structure Map use the same concept. When applied properly, it is not a bad thing.


God I hate IoC frameworks.

Integration/deployment is (in my experience) the source of the worst risk during a software project. We've gotten good at mitigating errors writing code, and testing code, but all of the runtime integration errors are still common to make and messy to debug. Worst yet, they happen close to release (either during integration or during deployment testing); causing the most impact.

IoC containers shift a whole pile of issues into this area. People end up voodoo debugging obscure configs rather than actual logic. Frameworks exist to get rid of that sort of code plumbing, not make more of it.

#17 ChaosEngine   Crossbones+   -  Reputation: 2499

Like
0Likes
Like

Posted 23 July 2012 - 07:32 PM


Can you give me an example of a "more advanced tool"? I agree that NMock isn't great, but I've found Rhino to be pretty good.


Typemock and JustMock come on the top of my mind. If you don't need sealed/non-virtual/static mocking, Rhino is a good enough framework. It's not like it is impossible to mock statics and .NET types, but that requires wrapping and additional work on your part (e.g. utility methods that call the unmockable static method and then mock that). Rhino works by subclassing, so you are in trouble if you need to mock private stuff (if memory serves correctly). That way the framework forces you to make the access level to protected or internal, which sucks a great deal. You might argue that you should not mock & unit test private methods. And you will be right. Yet there are some legitimate cases for that (where you want to hide implementation details in utility methods), but overall it is a bad idea. Nevertheless, I don't like the mocking framework to dictate higher visibility than what is reasonably required.


Cheers, will have a look into that.

Casting is the same stuff as QueryInterface. I don't see the functional difference between

var sth = baseInstance.GetExtension<IExtension>();
if (sth != null)
{
	 this.DoExtension(sth);
}
and
var sth = baseInstance as IExtension;
if (sth != null)
{
	 this.DoExtension(sth);
}
...

I understand that they're functionally the same, I don't like either approach. It feels like a code smell. See Telastyns answer above.



IoC containers shift a whole pile of issues into this area. People end up voodoo debugging obscure configs rather than actual logic. Frameworks exist to get rid of that sort of code plumbing, not make more of it.


Not really. Castle allows you to put the interface mapping in code.
[source lang="csharp"]container.Register( Component.For<IService>() .ImplementedBy<Service>() );[/source]
It's pretty trivial and in my current project of ~500k lines, we've never had any significant issues with it. The app calls the installer for the service at startup and that's it. Meanwhile the unit test framework does the standard mocked IoC thing and makes it pretty easy to test.

YMMV, of course. :D
if you think programming is like sex, you probably haven't done much of either.-------------- - capn_midnight

#18 dilyan_rusev   Members   -  Reputation: 1068

Like
0Likes
Like

Posted 24 July 2012 - 03:30 AM

I understand that they're functionally the same, I don't like either approach. It feels like a code smell. See Telastyns answer above.


I don't know of a good way to extend an OO API over time that does not involve something ugly. The examples I have are Win32, DirectX (COM) and Eclipse. They've all done it responsibly (meaning screwing with clients as little as possible), but the end result isn't particularly beautiful. Yet we need some way to not break our clients' code every time we need to change the interface. Not everybody has IBM's and Microsoft's resources, so you can't duplicate stuff (COM/Win32), and the only reasonable way that I know of is to add interface extensions (Eclipse). If you are lucky enough so that your change won't break anything - then sure, don't use them. If you have any ideas in that department - I would love to hear them.

About the IoC thingy. There is the dark realm of Unity's method rewriting. I can't seem to remember the proper way to call it, nor can I find anything on google to demonstrate it. The idea is that you can configure your class so that when certain methods (e.g. virtual with a certain matching pattern, like all Get* methods), Unity will create something like a chain of methods to be called when the original method is called instead. Your original implementation is just one of the methods that are going to be called. This enables you to do things like automatic security checks, but it is a nightmare to debug. This is because of the way it is implemented - Unity emits a new assembly where they inherit your class. They override the methods for which you want this behavior, and emit some code in order to implement this. The problem stems from the fact that, when you step into your method, you go right into Unity's resolution logic, some of which is undebuggable (I.e. dynamically emitted IL). So in order to test your original logic, you have to set breakpoints into the body of your original method, you can't just "step in". And this is just one of the many joys if you use the more advanced features of IoC frameworks.

Edit:
Oh yes, it is called method interception. Please make the world a favor and avoid it as much as possible Posted Image

Edit2:
They don't override just the configured methods, but everything virtual. Happy debugging :D

Edited by dilyan_rusev, 24 July 2012 - 03:33 AM.





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS