Pure virtual, or abstract, methods are those that are defined with a signature and no body, and must be implemented by some base class definition before you can instantiate an instance of the object.
One of the key concepts in the test first programming paradigm is to write as little code as possible in order to compile test cases that fail. If you’re not a test-first programming addict like I am, the concept takes a little getting used to, and I won’t try to explain it here. Suffice it to say you should do some reading and try it out, you’ll like it.
I’ve come to the conclusion that pure virtual methods force you to write more code than is necessary for a test to fail, and therefore violate good test-first programming techniques. The reason being that in order for your test to fail, you must also implement some base class definition that implements your pure virtual methods so that you can instantiate an instance in order to compile your tests.
Instead, simply write virtual methods with default implementations that throw an exception. Here’s an example (in C# using NUnit) of how one would write a test using a pure virtual function:
[TestFixture]
public class MyAbstractClassTester {
[Test]
public void MethodWorksTest() {
MyAbstractClass mac = new MyConcreteBaseClass();
mac.DoSomething();
}
}
public abstract class MyAbstractClass {
public MyAbstractClass() {
}
public abstract void DoSomething();
}
public class MyConcreteBaseClass : MyAbstractClass {
public MyConcreteBaseClass() : base() {
}
public override void DoSomething() {
throw new ApplicationException( "Method not implemented.");
}
}
In this trivial example you can see that the only reason we implemented DoSomething in the base class was because we had too. While it’s only a few extra lines of code and doesn’t seem like a big deal, it’s more than I had to do in order to make my test fail. Here’s how you can do it more simply:
[TestFixture]
public class MyAbstractClassTester {
[Test]
public void MethodWorksTest() {
MyAbstractClass mac = new MyConcreteBaseClass();
mac.DoSomething();
}
}
public abstract class MyAbstractClass {
public MyAbstractClass() {
}
public virtual void DoSomething() {
throw new ApplicationException( "Method not implemented." );
}
}
public class MyConcreteBaseClass : MyAbstractClass {
public MyConcreteBaseClass() : base() {
}
}
As you can see, having the DoSomething() method be virtual in the base class with a default implementation means I’ve written less code, and my tests are equally valid.
Now I can hear many of you thinking that “the use of virtual methods rather than abstract methods opens you to the possibility of not implementing required functionality.” While this is an valid initial argument, remember, if you’re doing test first programming correctly, you rely on your tests to tell you what’s working and what isn’t. Your tests won’t pass if you don’t implement the required functionality. All you’re doing is moving the requirement that the method be implemented out of compile time and into the test phase – and it could be argued that testing is the more appropriate place for it. You’ll still see the error glaring at you through the bright red bar, AND you’ll have the context in which the error occurred.
This further drives home the goal of writing just enough code to make your tests fail, and then only implementing exactly what’s required to make your tests pass.