Over a million developers have joined DZone.

Keep Public Interfaces Away From Children

· Agile Zone

Learn more about how DevOps teams must adopt a more agile development process, working in parallel instead of waiting on other teams to finish their components or for resources to become available, brought to you in partnership with CA Technologies.

It is natural to think of the public methods and properties of a class making up 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.

It still makes sense to offer the key validation to child classes as it will be a common requirement to validate the key for several types of cars for several operations. With a protected non-virtual method the service can be provided to child classes only.

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.

Discover the warning signs of DevOps Dysfunction and learn how to get back on the right track, brought to you in partnership with CA Technologies.

Topics:

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}