Abstraction is everything

Recently I had a discussion with someone where I bluntly stated I didn’t like virtual functions. Now that I’m reading more about software design principles I can formulate myself a bit better. After a few quick google searches, I couldn’t find the following argumentation anywhere, so I thought it might be relevant to post about it.

First, to rephrase myself: “Prefer pure virtual functions over non-pure virtual functions where possible.” As a quick refresher, pure virtual functions are required to be implemented by a derived class, while non-pure aren’t. Classes containing pure virtual functions are called abstract classes.

In good tradition I’ll start by quoting a design principle to which my statement relates.

Open/Closed Principle: Software entities should be open for extension, but closed for modification. – Bertrand Meyer

More specifically, the “Polymorphic Open/Closed Principle”. This definition advocates inheritance from abstract base classes. The existing interface is closed to modifications and new implementations must, at a minimum, implement that interface.

Although most people understand abstract classes, and how it relates to the template method pattern, I often see implementations relying on overridable functions instead. I rarely use non-pure virtual methods. Only after considering all other possibilities, I might find an overridable virtual method the cleanest approach.

Consider the following C# example taken from the msdn documentation on override:

public class Square
{
    public double x;

    public Square( double x )
    {
        this.x = x;
    }

    public virtual double Area()
    {
        return x * x;
    }
}

class Cube : Square
{
    public Cube( double x ) : base( x )
    {
    }

    public override double Area()
    {
        return 6 * base.Area();
    }
}

I understand that the sole intent of the example is to demonstrate the usage of the override keyword, so I’m not criticizing it. It just serves as a nice example where I find an other approach to be a better implementation. I also added additional functionality to highlight the Open/Closed Principle.

 

public abstract class AbstractShape
{
    public abstract double Area();

    public override string ToString()
    {
        return GetType().ToString() + ": area " + Area();
    }
}

public class Square : AbstractShape
{
    private double _sideLength;

    public Square( double sideLength )
    {
        _sideLength = sideLength;
    }

    public override double Area()
    {
        return _sideLength * _sideLength;
    }
}

public class AbstractCompositeShape : AbstractShape
{
    private List<AbstractShape> _childShapes;

    protected AbstractCompositeShape( List<AbstractShape> shapes )
    {
        _childShapes = shapes;
    }

    public override double Area()
    {
        double totalArea = 0;
        foreach ( AbstractShape shape in _childShapes )
        {
            totalArea += shape.Area();
        }
        return totalArea;
    }
}

public class Cube : AbstractCompositeShape
{
    private Cube( List<AbstractShape> sides ) : base( sides )
    {
    }

    public static Cube FromSideLength( double sideLength )
    {
        List<AbstractShape> sides = new List<AbstractShape>();
        for ( int i = 0; i < 6; ++i )
        {
            sides.Add( new Square( sideLength ) );
        }
        return new Cube( sides );
    }
}

Of course this example shouldn’t be considered as a real world example. An implementation is mainly dependant on its usage. This example could be useful where unique identification of every face is important, and where a lot of other shapes are expected. Just to state a few things that would also be worth considering: generics, localization, performance, …

The second sample prevents misuse and promotes reuse, which are advantages of following the Open/Closed Principle like this. You can’t forget to implement a required function. It also demonstrates there are more alternatives to virtual functions than just abstract functions. Composition was used here to achieve the same goal as the first example.

As a set of guidelines I would specify:

  • When a function always needs a custom implementation in an extending class, never use non-pure virtual functions.
  • When it is possible to group a certain default implementation in a subclass, do so instead of defining it as a default non-pure virtual function. (e.g. Area() function of AbstractCompositeShape in the example above.)
  • Question yourself why the behavior of a function needs to be changed. Can this new behavior be encapsulated? (e.g. Usage of composition in AbstractCompositeShape.)
  • Not overriding a certain virtual method shouldn’t have major functional consequences. (e.g. ToString method.)

The lead architect of C#, Anders Hejlsberg, explains why non-virtual is default in C#, as opposed to Java. He also advises caution when making functions virtual.

UPDATE
A more Java oriented argumentation can be found here.

About these ads
  1. #1 by Justin on February 9, 2011 - 8:52 pm

    If you’re going to link to your blog from StackOverflow, you should have the courtesy to link back to it and not a mirror site. Your java link in your final update should be http://stackoverflow.com/questions/3050811/virtual-functions-should-not-be-used-excessively-why

    • #2 by Steven Jeuris on February 9, 2011 - 8:59 pm

      Thanks for the heads up. Didn’t notice that!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: