Abstract base class over interface
Currently I'm reading the book Framework Design Guidelines. This is one of the best books I have read in some time. Most books that cover design in general are one-sided and high-lights the author's beliefs and convictions. However, this book is very different. It gives suggestions about various framework design aspects and at the same time high-lights views/opinions of different people in the .NET team which doesn't necessarily conform to the guidelines. This makes the book a very interesting read.
One of the suggestions is "Favor defining classes over interface". While this is highly debatable, I agree to this in general. In this section I read a comment from Brian Pepin that reminded me of some framework code I saw long time back which convinced me that Abstract Base Classes are sometimes much superior to interfaces in defining contracts.
In that UI framework one of the requirements was that individual controls should support loading bitmaps that act as application skins with the following method prototypes....
void LoadBitmap(string fileName);
void LoadBitmap(string fileName, Color transparentCol);
void LoadBitmap(string fileName, int width, int height);
void LoadBitmap(string fileName, Color transparentCol, int width, int
height);
The last method is the actual implementation and all the other methods fill in default values and passes it to this method.
In case this was defined in an interface as in
public interface ILoadBitmap{
void LoadBitmap(string fileName);
void LoadBitmap(string fileName, Color transparentCol);
void LoadBitmap(string fileName, int width, int height);
void LoadBitmap(string fileName, Color transparentCol,
int width, int height);
}
All classes that implemented the interface had to do method parameter validation for all the 4 methods and call the last method passing the default value for the parameters not specified. Not only this becomes tedious if some 20 types of controls supported skinning, it leads to programmer error in which a wrong default value is passed in some of the controls. This is where Abstract Base Class comes in. Using ABC you can code this as
public abstract class SkinControl{
public void LoadBitmap(string fileName)
{
Debug.Assert(!string.IsNullOrEmpty(fileName));
LoadBitmap(fileName, Color.Magenta, -1, -1);
}
public void LoadBitmap(string fileName, Color transparentCol)
{
Debug.Assert(!string.IsNullOrEmpty(fileName));
LoadBitmap(fileName, transparentCol, -1, -1);
}
public void LoadBitmap(string fileName, int width, int height)
{
Debug.Assert(!string.IsNullOrEmpty(fileName));
Debug.Assert(width > 0 && height > 0);
LoadBitmap(fileName, Color.Magenta, width, height);
}
public abstract void LoadBitmap(string fileName,
Color transparentCol,
int width, int height);
}
public class SkinnedButton : SkinControl{
public override void LoadBitmap(string fileName,
Color transparentCol,
int width, int height)
{
// Actual implementation
}
}
All the methods are implemented in the ABC and only the last method is made abstract. So for all classes that implements this abstract class the developer needs to implement the fourth overload of the method only. Most of the contract is directly coded into the ABC. This results in less code and less programming error.
Comments
- Anonymous
February 13, 2006
The comment has been removed - Anonymous
February 13, 2006
The comment has been removed - Anonymous
February 13, 2006
The way this is often done in Java is to define an interface and have a 'default' class implement the interface (called an adapter).
As interfaces define a contract I much prefer to see them used when, for example, declaring method parameters, or when you need to define some sort of RPC mechanism.
If you go with the interface with the default implementation you can stick to using interface based design that can be used in so many areas, and still have an implementation that can easily be re-used. - Anonymous
February 13, 2006
Consider what type you should accept as a parameter. If you accept an interface, you open up the possibility of the developer extending your interface with their own, allowing them to do interface based programming all the way down through your API. If, on the other hand, you only accept a class (abstract or not) then the developer has no way of writing the following code:
public void DoSomething(IMyExtendedInterface obj)
{
YourAPI.DoSOmething(obj);
}
which is really a pity, since code that is written close to your API should be able to raise the level of abstraction. - Anonymous
February 13, 2006
TAG as MIKE had pointed out the point is to highlight ABC over interface and not show a true implementation. Thats why there is just a Debug.Assert. Most sample code do not even have error handling as it sidetracks the point being expressed. I had the debug.assert to indicate that parameter verification is also being handled in the ABC.... - Anonymous
February 13, 2006
If C# supports "default interface method implementation" (which enables you to add default implementation for an interface method - but the method is still virtual, and the interface still cannot contain instance data), then you will be using interface instead of base class because interface is extensible. Anders actually has talked about this before - hope it be added one day in the future (beyond C# 3.0). - Anonymous
February 21, 2006
You're in vacation? I didn't see more posts from you... - Anonymous
February 21, 2006
The comment has been removed - Anonymous
February 21, 2006
:D
Started not...I check your blog every day! - Anonymous
October 15, 2006
Whether you use an interface or an abstract base class should depend on what you want to achieve.If you design a framework, users of your framework should only see interfaces in my opinion, that are instanciated by your framework by some sort of factory mechanism.For doing something like Error Handling I prefer the Security Facade - which just implements a specific interface with doing the error handling and then calls the implementor via the same interface. This concept is more flexible I think, because the implemented classes do not have a dependency on the error handling but can be "parametrized" with the Error handling through the Security Facade.But: All depends on the context. Do not over-engineer simple problems :-)