11

If we assume that it is not desirable for the base class to be a pure interface class, and using the 2 examples from below, which is a better approach, using the abstract or virtual method class definition?

  • The advantage of "abstract" version is that it is probably looks cleaner and forces the derived class to give a hopefully meaningful implementation.

  • The advantage of the "virtual" version is that it can be easily pulled in by other modules and used for testing without adding a bunch of underlying framework like the abstract version requires.

Abstract Version:

public abstract class AbstractVersion
{
    public abstract ReturnType Method1();        
    public abstract ReturnType Method2();
             .
             .
    public abstract ReturnType MethodN();

    //////////////////////////////////////////////
    // Other class implementation stuff is here
    //////////////////////////////////////////////
}

Virtual Version:

public class VirtualVersion
{
    public virtual ReturnType Method1()
    {
        return ReturnType.NotImplemented;
    }

    public virtual ReturnType Method2()
    {
        return ReturnType.NotImplemented;
    }
             .
             .
    public virtual ReturnType MethodN()
    {
        return ReturnType.NotImplemented;
    }

    //////////////////////////////////////////////
    // Other class implementation stuff is here
    //////////////////////////////////////////////
}
8
  • Why are we assuming an interface is not desirable? Feb 26, 2013 at 17:54
  • Without a partially problem, it's hard to say one is better than the other.
    – Codism
    Feb 26, 2013 at 17:55
  • @Anthony: An Interface is not desirable because there is useful functionality that will also go into this class.
    – Dunk
    Feb 26, 2013 at 19:18
  • 4
    return ReturnType.NotImplemented? Seriously? If you can't reject unimplemented type at compile time (you can; use abstract methods) at least throw an exception.
    – Jan Hudec
    Feb 27, 2013 at 12:06
  • 3
    @Dunk: Here they are necessary. Return values will go unchecked.
    – Jan Hudec
    Feb 28, 2013 at 14:27

5 Answers 5

15

My vote, if I were consuming your stuff, would be for the abstract methods. That goes along with "fail early." It may be a pain at declaration time to add all methods (though any decent refactoring tool will do this quickly), but at least I know what the problem is immediately and fix it. I'd rather that than be debugging 6 months and 12 people's worth of changes later to see why we're suddenly getting a not implemented exception.

3
  • Good point regarding unexpectantly getting the NotImplemented error. Which is a plus on the abstract side, because you'll get a compile-time instead of run-time error.
    – Dunk
    Feb 26, 2013 at 19:43
  • 3
    +1 - In addition to failing early, inheritors can see immediately what methods they need to implement via "Implement Abstract Class" rather than doing what they thought was enough and then failing at runtime.
    – Telastyn
    Feb 26, 2013 at 22:02
  • I accepted this answer because failing at compile time was a plus that I failed to list and because of the reference to using the IDE to auto implement the methods quickly.
    – Dunk
    Feb 28, 2013 at 14:17
25

The virtual version is both bug prone and semantically incorrect.

Abstract is saying "this method isn't implemented here. you must implement it to make this class work"

Virtual is saying "I have a default implementation but you can change me if you need"

If your ultimate objective is testability then interfaces are normally the best option. (this class does x rather than this class is a x). You might need to break your classes into smaller components though for this to work nicely.

3

This depends on the usage of your class.

If the methods have some reasonable “empty” implementation, you have lots of methods and you often override just a few of them, then using virtual methods makes sense. For example ExpressionVisitor is implemented this way.

Otherwise, I think you should use abstract methods.

Ideally, you shouldn't have methods that are not implemented, but in some cases, that is the best approach. But if you decide to do that, such methods should throw NotImplementedException, not return some special value.

2
  • I would note that "NotImplementedException" often indicates an error of omission, whereas "NotSupportedException" indicates an overt choice. Other than that, I agree. Feb 26, 2013 at 22:26
  • It's worthwhile to note that many methods are defined in terms of "Do whatever is necessary to satisfy any obligations related to X". Invoking such a method on an object that has no items related to X may be pointless, but would still be well-defined. Further, if an object may or may not have any obligations related to X it's generally cleaner and more efficient to unconditionally say "Satisfy all your obligations related to X", than to first ask if it has any obligations and conditionally ask it to satisfy them.
    – supercat
    Feb 21, 2015 at 21:02
1

I would suggest that you reconsider having a separate interface defined which your base class implements, and then you follow the abstract approach.

Imaging code like this:

public interface IVersion
{
    ReturnType Method1();        
    ReturnType Method2();
             .
             .
    ReturnType MethodN();
}

public abstract class AbstractVersion : IVersion
{
    public abstract ReturnType Method1();        
    public abstract ReturnType Method2();
             .
             .
    public abstract ReturnType MethodN();

    //////////////////////////////////////////////
    // Other class implementation stuff is here
    //////////////////////////////////////////////
}

Doing this solves these problems:

  1. By having all code that uses objects derived from AbstractVersion can now be implemented to receive IVersion interface instead, This means that they can be more easily unit tested.

  2. Release 2 of your product can then implement an interface IVersion2 to provide additional functionality without breaking your existing customers code.

eg.

public interface IVersion
{
    ReturnType Method1();        
    ReturnType Method2();
             .
             .
    ReturnType MethodN();
}

public interface IVersion2
{
    ReturnType Method2_1();
}

public abstract class AbstractVersion : IVersion, IVersion2
{
    public abstract ReturnType Method1();        
    public abstract ReturnType Method2();
             .
             .
    public abstract ReturnType MethodN();
    public abstract ReturnType Method2_1();

    //////////////////////////////////////////////
    // Other class implementation stuff is here
    //////////////////////////////////////////////
}

It's also worth reading up about dependency inversion as well, to prevent this class from containing hard coded dependencies that prevent effective unit testing.

2
  • I upvoted for providing the ability to handle different versions. However, I've tried and tried again to make effective use of interface classes, as a normal part of design and always end up realizing that the interface classes either provides no/little value and actually obsfuscates the code instead of making life easier. It is very seldom that I would have multiple classes inherit from another, like an interface class, and there isn't a fair amount of commonality that isn't shareable. Abstract classes tend to work better as I get the built in shareable aspect that interfaces dont provide.
    – Dunk
    Jan 22, 2014 at 15:16
  • And using inheritance with good class names provides a much more intuitive way to easily understand the classes (and thus the system) than a bunch of functional interface class names that are difficult to tie together mentally as a whole. Also, using interface classes tends to create a ton of extra classes, making the system harder to understand in yet another way.
    – Dunk
    Jan 22, 2014 at 15:36
-2

Dependency injection relies on interfaces. Heres a short example. Class Student has a function called CreateStudent that requires a parameter that implements interface "IReporting" (with a ReportAction method). After it creates a student it calls ReportAction on the concrete class parameter. If the system is set up to send an email after creating a student we send in a concrete class that sends an email in its ReportAction implementation, or we could send in another concrete class that sends output to a printer in its ReportAction implementation. Great for code re-use.

1
  • 1
    this doesn't seem to offer anything substantial over points made and explained in prior 5 answers
    – gnat
    May 21, 2015 at 13:05

Not the answer you're looking for? Browse other questions tagged or ask your own question.