31

Is an interface + extension methods (mixin) preferable to an abstract class?

If your answer is "it depends", what does it depend upon?

I see two possible advantages to the interface + extension approach.

  • Interfaces are multiply inheritable and classes are not.
  • You can use extension methods to extend interfaces in a non-breaking way. (Clients that implement your interface will gain your new base implementation but still be able to override it.)

I have not yet thought of a downside to this approach. There may be a glaringly simple reason that the interface + extension approach will fail.

Two helpful articles on this topic are

dss539
  • 6,804
  • 2
  • 34
  • 64
  • 1
    I don't yet see a definitive answer for this. So far, I believe that Jon's response is the most helpful, but be sure to read Stefan Steinegger's answer as well. They both make important points that should be considered before picking a method. My understanding is now "Need to use inheritance to override methods? Use an abstract base. Else use interface + extension." – dss539 Apr 29 '09 at 15:50

3 Answers3

25

Downside of extension methods: clients pre-C#3/VB9 won't be able to use it as easily.

That's about it as far as I'm concerned - I think the interface-based approach is significantly nicer. You can then mock out your dependencies nicely, and everything is basically less tightly coupled. I'm not a huge fan of class inheritance unless it's really about specialization :)

EDIT: I've just thought of one other benefit which might be relevant. It's possible that some of the concrete implementations could provide more optimized versions of some of the general methods.

Enumerable.Count is a good example of this - it explicitly checks whether the sequence implements IList or not, because if it does it can call Count on the list instead of iterating through the whole sequence. If IEnumerable<T> had been an abstract class with a virtual Count() method, it could have been overridden in List<T> rather than there being a single implementation which knows about IList explicitly. I'm not saying this is always relevant, nor that IEnumerable<T> should have been an abstract class (definitely not!) - just pointing it out as a small possible disadvantage. That's where polymorphism really would be appropriate, by specializing existing behaviour (admittedly in a way which only affects performance instead of the result).

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Thanks, Jon. "concrete implementations could provide more optimized versions" Can't the concrete version do that in both approaches? "an extension method will never be called if it has the same signature as a method defined in the type" http://msdn.microsoft.com/en-us/library/bb383977.aspx Sounds like you can override extension methods in the concrete class anyways. I suppose if the client casts it to IBase then it won't, but that's the same with the abstract class approach isn't it? – dss539 Apr 23 '09 at 21:25
  • 3
    No, you can't really override them. You can provide methods with the same signature and then they'll get called *if* the caller knows about them - but they won't be called *polymorphically* based on execution-time type. – Jon Skeet Apr 23 '09 at 21:30
  • 1
    You cannot mock out if you have the logic in extension methods. Of course you mock out the interface, but the static extension method is still there. It is not a member of the interface. So you don't really work with interfaces, but with static methods. I don't say extension methods are bad, but your arguments about coupling are rather weak. – Stefan Steinegger Apr 23 '09 at 22:42
  • 1
    Mocking out the interfaces is precisely what I was talking about. Yes, you end up being coupled to the static method implementations to some extent (although you could write test helpers which effectively did the same thing to a mock, providing specified results, so that it's all encapsulated in one place). You're still decoupling the caller from the *real* code which would have been in the concrete derived class. Yes, you could mock the abstract class - but IME that's uglier than interfaces. – Jon Skeet Apr 24 '09 at 05:42
  • It is also entirely possible that a generic extension method on an interface accepts a constrained type argument which it then passes to someone else. AFAIK, it is impossible to do this by any other means (with so much type safety and loose coupling). – Ani Mar 01 '12 at 15:13
  • @JonSkeet how do you feel about this 7 years later? I've stuck to the "old way" of doing things since it's probably easier for the average dev to understand. Have you learned anything, or has C# added anything, since 2009 that changes your view? – dss539 Jan 15 '16 at 20:47
  • @dss539: Well I still rarely use abstract classes... but like all things, basically "it depends". There's nothing in recent versions of C# which have affected this particularly, that I can think of right now. – Jon Skeet Jan 16 '16 at 09:34
23

IMHO, this is the wrong question.

You should use everything for what it is designed for.

  • Extension methods are not members. They are syntactically looking like members, so you can find them easier.
  • Extension methods can only use the public (or internal) interface. Many other classes could do the same. So extension methods are not a real encapsulation in an oo way.
  • They are static methods and can not be overridden and not be mocked in a unit test. Is is a non-OO language feature and the caller is statically bound to it.

  • Abstract base classes are really often misused to "reuse code" (instead of a real inheritance). This applies to inheritance in general.

The question should be: "When should I use Interfaces, extension methods or base classes?"

  • use interfaces whenever you need a contract (and this happens all the time).
  • Use (abstract) base classes when you have a situation for real inheritance (you could write a book about how to judged that, so I just leave it like this). An interface is mostly also implemented at the same time.
  • use extension methods for logic that should not be actually a member of the type, because it is not the responsibility of the type to implement it - but you want to make it easy to find and and it feels natural to call it like a member.

Edit:

Or the question should be: "How do I write reusable functionality that does not belong to a base class?"

  • write an interface that exposes the functionality
  • write a reusable library class that implements the functionality
  • write a class that implements the interface and reuses the functionality by aggregating the reusable class.

In general I would say, extension methods are the wrong place for business logic, except of special cases or special design decisions.

Base classes are only in rare cases the right decision. In doubt, it is not. In no-doubt, you should think again about it.

Stefan Steinegger
  • 63,782
  • 15
  • 129
  • 193
  • Extension methods are not real encapsulation? Perhaps I don't understand, but how does it break encapsulation? Are you saying that extension methods prevent an object from having state information that is private? In any case, your advice about when to use interface vs base class is extremely contrary to Krzyzstof Cwalina's advice. http://en.csharp-online.net/.NET_Type_Design_Guidelines%E2%80%94Choosing_Between_Class_and_Interface . – dss539 Apr 24 '09 at 13:24
  • Your point about 'using something as it is designed' is important because using something as it was designed usually increases readability. +1 However, using something in ways the designers did not understand may actually be better than using it the way they intended (in some cases, not all). – dss539 Apr 24 '09 at 13:27
  • This article is kind of strange. There are API's only based on interfaces, like NHibernate, and many others. It makes it very testable (a thing MS doesn't know much) and decouples your domain from the classes that implement some functionality you use. "Always implement against interfaces" is a guideline many people follow. - It's probably different for .NET framework classes, but i don't know why it should be different there. – Stefan Steinegger Apr 24 '09 at 14:11
  • 4
    @dss539, your first question about encapsulation: extension methods are just outside of the class. The extension method cannot access the state that is private. They "break" the encapsulation only if you are forced to make things public that should actually be private only because a extension method needs to access it. – Stefan Steinegger Apr 25 '09 at 19:05
  • What should be the alternative to abstract classes for reusing code among related classes? – theonlygusti Sep 09 '20 at 20:51
1

Interfaces tend to make code a little cleaner I feel it's a little easier to test. When you add Extensions your adding even more flexibility while keeping clean testable code.

For me abstract classes have always seemed clunky, using interfaces I can have an object factory that returns an object that is specific to what I'm trying to accomplish (separation of concerns).

Just making something up A have the interface called Math that has add, subtract, divide and multiply and then I have a class called IntMAth that implements Math that is optimized for integer math, and I have another class called FloatMath the implements Math that is optimized for Floating Math, and I have a generalMath that implements Math that handles everything else.

When I need to Add some floats I could call my factory MathFactory.getMath(typeof(float)) and it has some logic to know that if the type I'm passing in is a float then it returns the FloatMath class.

This way all of my classes are smaller and more maintainable, the code that calls the classes is smaller, etc.

Bob The Janitor
  • 20,292
  • 10
  • 49
  • 72