Keep Public Interfaces away from Children

It is natural to think of the public methods and properties of a class as the public interface of the class. When implementing a class that is meant to be derived there is also another interface – the one meant for child classes. A clear separation of the two interfaces makes the code cleaner. The one construct to avoid is public virtual methods.

As an example, consider the following implementation for a car.

abstract class Car
{
    private static Random keyGenerator = new Random();
 
    private readonly int keySignature = keyGenerator.Next();
 
    public string Driver { get; set; }
 
    public void Start(Key key)
    {
        CheckSeat();
        CheckMirrors();
        if (BeforeStartEngine != null)
        {
            BeforeStartEngine(this, new EventArgs());
        }
        StartEngine(key);
    }
 
    public event EventHandler<EventArgs> BeforeStartEngine;
 
    protected bool IsKeyApproved(Key key)
    {
        return key.KeySignature == keySignature;
    }
 
    protected abstract void StartEngine(Key key);
 
    protected virtual void CheckSeat()
    {
        Debug.WriteLine("Checking seat settings for driver {0}", Driver);
    }
 
    protected virtual void CheckMirrors()
    {
        Debug.WriteLine("Checking mirrors settings for driver {0}", Driver);
    }
}

In this class there is one public method (Start), a public property (Driver) and a public event (BeforeStartEngine) that makes up the public interface. There are also a number of protected methods that makes up the interface for child (derived) classes.

The Public Interface

Let’s start with the public interface. It contains a start method, which outlines the main algorithm for starting the car. First some settings have to be checked, then the engine is started. This algorithm is obligatory for all Car implementations. It is not possible to change the algorithm, however there is a public extension point in the BeforeStartEnginge event that anyone can subscribe to. The subscriber can make additional actions, but there is no way to alter the basic behaviour of the start algorithm itself. The steps of the algorithm are possible to customize by sub classes.

Customizing Operations in Sub Classes

There are three operations that can be customized by sub classes: StartEngine, CheckSeat, CheckMirrors. The child classes may or may not override the default behaviour of the Check* methods. All child classes must provide an own implementation of the engine start routine. An example is an antique car.

class AntiqueCar : Car
{
    protected override void StartEngine(Key key)
    {
        Debug.WriteLine("Hand cranking car to start");
    }
 
    protected override void CheckMirrors()
    {
        Debug.WriteLine("Ignoring mirror check - there are no mirrors.");
    }
}

Services for Sub Classes

So far we’ve treated the overridable protected methods that offer extension and modification points of the base class. There might also be a reason to provide certain services that are only available to child classes and not other classes as public methods are. In the car code example, the routine to check if a key is valid is such a services. It is protected, but non virtual, so it cannot be changed. It is a primitive operation that is not exposed to external clients of the class as a public method. To the clients, the validation of the key is part of larger operations such as engine start or opening a door.

class ElectricCar : Car
{
    protected override void StartEngine(Key key)
    {
        if (!IsKeyApproved(key))
        {
            throw new SecurityException("Invalid key");
        }
    }
}

It makes sense to offer the key validation service to child classes as it will be a common requirement to validate the key for several types of cars for several operations. What specific operations that do require key validation (if any at all – the antique car has no key) is up to the specific car to decide. That makes the key validation a perfect service to provide to the child classes, but leaving the choice to call it or not completely to the child class. A protected non-virtual method provides the service to the child classes, without exposing it publicly.

Beware of public virtual

The one thing that is not present in this code sample is public virtual or public abstract. I try to avoid using those, as they are both part of the public interface as well as being extension points. There will often be some common steps that have to be enforced each time a public method is called (parameter validation, write log messages). If a method is public virtual and the child class implementation forgets the base call, the enforcement is lost.

With separation of the public and the protected interface the base class can act as an enforcer of contracts. It can make safe for the child class to assume that all relevant pre conditions are checked before a virtual method is call. It can make safe for clients that key steps of the algorithm (such as security validation or logging) are impossible to skip over by a bad sub class implementation.

It really is a matter of separation of concerns. The public interface and the child class interface are separate concepts that should be clearly separated.

  • Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1179 on 2012-08-31
  • trans on 2012-08-31

    Private methods are the bane of programming. It’s a total misconception that the creator of a class should ever consider what methods a subclass should and should not be able to override.

    My best advice for those that think I am wrong…. learn Ruby.

  • Chris Marisic on 2012-08-31

    I don’t really agree with your views on public virtual. As long as your class respects the Open-Closed principle there is absolutely no downside to using public virtual.

    Also public virtual is generally one of the most important things for creating testable code. If you don’t go with public virtual, you will end up with hundreds of meaningless interfaces to be able to mock anything. Interfaces only need to be used where you swap implementations, all other usages generally are best as public virtual so you can rewrite any method you need for testing purposes.

    • Jason on 2012-08-31

      And that is the problem I have with test-first mentalities – you end up writing less maintainable/well-structured code in order to make it testable, mockable, etc. With all the “re-writing of methods for testing purposes” are you really testing the actual system at all? (Ok, sure you rewrite one method to unit test functionality in another method in isolation – but production systems don’t run in isolation…)

      • Chris Marisic on 2012-09-04

        “With all the “re-writing of methods for testing purposes” are you really testing the actual system at all?”

        Absolutely not. That’s not the purpose of unit testing and test first development.

        That’s the purpose of integration testing. Integration testing is done against full app, real database, real web browser (go go Selenium WebDriver).

        Unit tests are to mitigate developer error or business analyst error in isolation, integration tests are to certify the app as a whole.

  • Fadi (itoctopus) on 2012-09-02

    I don’t agree about private methods – sometimes you don’t want methods to be accessed directly – and this is especially true when you’re building a black box interface – where you just want people to know about your public functions and use them.

    I don’t use them heavily but I do use them.

    PS: This post made me research the .nu TLD – interesting information about that little island near New Zealand.

    • Anders Abel on 2012-09-03

      Regarding the Ps: The .nu TLD was popular in Sweden 10+ years ago, when there were very strict rules governing the .se TLD. Registration under .se was only available to companies and only under their legal name. I’ve had this domain since -99 for mail hosting, so it was natural to keep on with it when I started the blog.

  • Leave a Reply

    Your name as it will be displayed on the posted comment.
    Your e-mail address will not be published. It is only used if I want to get in touch during comment moderation.
    Your name will be a link to this address.
Software Development is a Job – Coding is a Passion

I'm Anders Abel, a systems architect and developer working for Kentor in Stockholm, Sweden.

profile for Anders Abel at Stack Overflow, Q&A for professional and enthusiast programmers

Code for most posts is available on my GitHub account.

Popular Posts

Archives

Series

Powered by WordPress with the Passion for Coding theme.