2

I have a project where quite a few functions and variable getters will be defined, abstractly. My question is should I use an abstract class for this(with each function throwing NotImplementedException), or should I just use an interface? Or should I use both, making both an interface and then an abstract class implementing the interface?

Note, even though all of these functions and such may be defined, it does not mean they will all be used in all use cases. For instance, AddUser in an authentication class may be defined in an interface, but not ever used in a website due to closed user sign up.

Earlz
  • 62,085
  • 98
  • 303
  • 499

7 Answers7

2

In general, the answer to the question of whether or not to use inheritance or an interface can be answered by thinking about it this way:

When thinking about hypothetical implementing classes, is it a case where these types are what I'm describing, or is it a case where these types can be or can do what I'm describing?

Consider, for example, the IEnumerable<T> interface. The classes that implement IEnumerable<T> are all different classes. They can be an enumerable structure, but they're fundamentally something else (a List<T> or a Dictionary<TKey, TValue> or a query, etc.)

On the other hand, look at the System.IO.Stream class. While the classes that inherit from that abstract class are different (FileStream vs. NetworkStream, for example), they are both fundamentally streams--just different kinds. The stream functionality is at the core of what defines these types, versus just describing a portion of the type or a set of behaviors that they provide.

Often you'll find it beneficial to do both; define an interface that defines your behavior, then an abstract class that implements it and provides core functionality. This will allow you to, if appropriate, have the best of both worlds: an abstract class for inheriting from when the functionality is core, and an interface to implement when it isn't.

Also, bear in mind that it's still possible to provide some core functionality on an interface through the use of extension methods. While this doesn't, strictly speaking, put any actual instance code on the interface (since that's impossible), you can mimic it. This is how the LINQ-to-Objects query functions work on IEnumerable<T>, by way of the static Enumerable class that defines the extension methods used for querying generic IEnumerable<T> instances.

As a side note, you don't need to throw any NotImplementedExceptions. If you define a function or property as abstract, then you don't need to (and, in fact, cannot) provide a function body for it within the abstract class; the inheriting classes will be forced to provide a method body. They might throw such an exception, but that's not something you need to worry about (and is true of interfaces as well).

Adam Robinson
  • 182,639
  • 35
  • 285
  • 343
  • I don't mind putting `throw new NotImplementedException` in my abstract class, what I DO NOT want is for people to have to do it when they derive from my class/implement my interface – Earlz Feb 12 '11 at 02:51
  • @Earlz: Then it isn't (by definition) an abstract class. Not that there's necessarily anything wrong with it, but that's not an abstract class. Note that Visual Studio will automatically add the appropriate overrides with the `NotImplementedException whenever someone inherits from an abstract class. It's usually better to do it this way, that way the developer doesn't have to go hunting to find everything that they need to override to prevent those exceptions from being thrown. But that's a design choice, in the end. – Adam Robinson Feb 12 '11 at 03:00
  • well what I'm talking about is basically having every method marked `virtual` in a class marked `abstract`. Sure, you could technically create an instance of the abstract class, but it'd be completely pointless to – Earlz Feb 12 '11 at 03:02
  • 1
    @Earlz: Gotcha. And actually, you couldn't (even if there are no abstract members, you still can't instantiate an abstract class), so the choice is still right even if you don't actually define any members as `abstract`. – Adam Robinson Feb 12 '11 at 03:06
1

If you're not providing any implementation, then use an interface otherwise use an abstract class. If there are some methods that may not be implemented in subclasses, it might make sense to create an intermediate abstract class to do the legwork of throwing NotSupportedException or similar.

spender
  • 117,338
  • 33
  • 229
  • 351
  • Well, the thing is that not all functions I want defined in the interface may be used in all areas where someone would derive from my class. – Earlz Feb 12 '11 at 02:38
  • the implementations could choose to not do anything in the methods that are not relevant. – TheZenker Feb 12 '11 at 02:41
  • 1
    It's an interesting question though. I recently subclassed `Stream`... the concept of read/write only steams means empty implementation of some methods is part of the design. I wonder if it could be done better. – spender Feb 12 '11 at 02:44
  • @spender, exactly. There are examples of this kind of thing in .Net itself, I can't remember which class I used like it, but I ended up having like 20 methods that I had to create with just `throw new NotImplementedException()` where it was an interface. I don't want for some user of my API to have to do that with my class/interface – Earlz Feb 12 '11 at 02:48
  • 1
    @spender: Very true. Stream is a good example of how confusing it can be, though - you end up having to implement a bunch of stuff like CanSeek/CanRead/CanWrite, etc. Would be interesting to try to design something around an abstract class (`Stream`) with various interfaces for behavior, instead, ie IStreamReadable, IStreamWritable, etc... – Reed Copsey Feb 12 '11 at 02:49
  • @Reed, hmm. Maybe I need to think outside the box a bit. Wouldn't this get pretty complex assuming you had a lot of different optional functions? `IStreamReadable, IStreamWriteable, IStreamForwardSeekable, IStreamBackwardSeekable, IStreamCountable` etc. Seems like for a fully implemented class, you'll have a ton of interfaces (which would also muck up the namespace a bit) – Earlz Feb 12 '11 at 02:55
  • Ah, regarding your edit. Yea, I meant to use `NotSupportedException` not `NotImplementedException` – Earlz Feb 12 '11 at 03:03
1

Personally, I think it depends on what the "type" is defining.

If you're defining a set of behaviors, I would recommend an interface.

If, on the other hand, the type really defines a "type", then I'd prefer an abstract class. I would recommend leaving the methods abstract instead of providing an empty behavior, though.


Note, even though all of these functions and such may be defined, it does not mean they will all be used in all use cases.

If this is true, you should consider breaking this up into multiple abstract classes or interfaces. Having "inappropriate" methods in the base class/interface really is a violation of the Liskov Substitution Principle, and a sign of a design flaw.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • What about for my example: An authentication module. You definitely want for their to be a consistent way to create a new user, but not all sites will want to use this functionality. How would you break any of that into a different class/interface? – Earlz Feb 12 '11 at 02:52
1

One advantage of abstract classes is that one can add to an abstract class new class members whose default implementation can be expressed in terms of existing class members, without breaking existing inheritors of that class. By contrast, if any new members are added to an interface, every implementation of that interface must be modified to add the necessary functionality.

It would be very nice if .net allowed for an interface to include default implementations for properties, methods, and events which did not make any use of object fields. From a technical standpoint, I would think such a thing could be accomplished without too much difficulty by having for each interface a list of default vtable entries which could be used with implementations that don't define all vtable slots. Unfortunately, nothing like that ability exists in .net.

supercat
  • 77,689
  • 9
  • 166
  • 211
0

Abstract classes should be used when you can provide a partial implementation. Use interfaces when you don't want to provide any implementation at all - just definition.

In your question, it sounds like there is no implementation, so go with an interface.

Also, rather than throwing NotImplementedException you should declare your method/property with the abstract keyword so that all inheritors have to provide an implementation.

Reddog
  • 15,219
  • 3
  • 51
  • 63
0

Another pattern that works in some situations is to create a base class that is not abstract. Its has a set of public methods that define the API. Each of these calls a Protected method that is Overideable. This allows the derived class to pick and choose what methods it needs to implement. So for instance

 public void AddUser(object user)
    {
        AddUserCore(user);
    }

    protected virtual void AddUserCore(object user)
    {
        //no implementation in base
    }
TheZenker
  • 1,710
  • 1
  • 16
  • 29
  • This seems to be about the same as an abstract base class – Earlz Feb 12 '11 at 02:56
  • 2
    Nitpick: you probably meant that protected method to be virtual. Also, a common (though not universal!) naming convention for this in the .NET Framework is AddUser/AddUserCore. – itowlson Feb 12 '11 at 02:56
  • @itowlson yes on the Core naming convention, I blanked on that, also on virtual I will edit (C# is not my first language ;-)) thanks for the tweaks – TheZenker Feb 16 '11 at 20:06
0

@Earlz I think refering to this: Note, even though all of these functions and such may be defined, it does not mean they will all be used in all use cases. is directly related to the best way to 'attack' this problem.

What you should aim at is minimizing the number of such functions so that it becomes irrelavant (or at least not that important) if you use either or. So improve the design as much as you can and you will see that it really doesn't matter which way you go.

Better yet post a high level of what you are trying to do and let's see if we can come up together with something nice. More brains working towards a common goal will get a better answer/design.