O'Reilly logo

Programming C# 4.0 by Jesse Liberty, Matthew Adams, Ian Griffiths

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Requiring Overrides with abstract

An abstract base class is intended to provide the “scaffolding” for a hierarchy of related classes, but it is not intended to be instantiated itself, because it isn’t “finished.” It requires that classes derived from it add in some missing bits.

Let’s turn our current firefighter into an abstract base for the others to use, and see how that works.

First, we can add the abstract modifier to the class itself, and see what happens:

abstract class Firefighter
{
    // ...
}

As usual, we add the modifier before the class keyword (and after any accessibility modifiers, if present).

If we build, we now get a compiler error:

Cannot create an instance of the abstract class or interface 'Firefighter'

That’s because we’re trying to create an instance of the Firefighter in our main function:

Firefighter joe = new Firefighter { Name = "Joe" };

This is no longer allowed, because the Firefighter class is now abstract.

OK, we’ll comment that out temporarily while we carry on refactoring. We want it to continue to build as we go so that we can see if we’ve introduced any other errors:

//Firefighter joe = new Firefighter { Name = "Joe" };
//joe.ExtinguishFire;

Build and run, and we get the output we expect—Bill is still spraying the water around:

Bill is putting out the fire!
There's water going everywhere!
Training the hose on the fire.

One other thing: if we’re creating an abstract base class, we usually name it something such as FooBase to distinguish it from a regular class. This is by no means a hard-and-fast rule, but it is pretty common. So let’s rename Firefighter to FirefighterBase, and make sure we change it where it is referenced elsewhere—on the Firetruck, FireChief, and TraineeFirefighter classes.

The easiest way to do that is to use the automatic rename refactoring in the IDE. Just type over the old name in the declaration, click on the Smart Tag that appears, and choose Rename Firefighter to FirefighterBase from the menu. You could do it by hand if you wanted, though.

The whole purpose of this was to get rid of the default implementation we have for putting out fires, so let’s turn Firefighterbase.ExtinguishFire into an abstract method.

Just like the modifier for the class, we use the abstract keyword, but this time we also remove the method body and add a semicolon at the end of the declaration:

abstract class FirefighterBase
{
    public abstract void ExtinguishFire();
}

If you try building again now, you can see that we have a new compiler error:

'TraineeFirefighter' does not implement inherited abstract member
'FirefighterBase.ExtinguishFire()'

Remember, we are required to override an abstract method; our class isn’t finished until we do so (unlike a virtual method, where we are invited to override it, but it will fall back on the base if we don’t). While our FireChief does override the method, our TraineeFirefighter doesn’t. So we need to add a suitable implementation:

 class TraineeFirefighter : FirefighterBase
 {
    // Override the abstract method
    public override void ExtinguishFire()
    {
        // What are we going to put here?
    }
    // ...
}

But what are we going to put into that ExtinguishFire override? Before, we depended on our base class for the implementation, but our base is now abstract, so we don’t have one available anymore!

That’s because we’ve forgotten about our regular Firefighter. Let’s add a class for him back into the hierarchy:

class Firefighter : FirefighterBase
{
    public override void ExtinguishFire()
    {
        Console.WriteLine("{0} is putting out the fire!", Name);
        TurnOnHose();
        TrainHoseOnFire();
    }
}

Notice we’ve given him the “standard firefighter” implementation for ExtinguishFire.

If we take one more look at the base class, we can see that we still have those two virtual implementation helpers. While everything builds correctly at the moment, they don’t really belong there; they are really a part of the Firefighter implementation, so let’s move them in there. We end up with the code in Example 4-13.

Example 4-13. Refactored base classes

abstract class FirefighterBase
{
    public abstract void ExtinguishFire();

    public string Name { get; set; }

    public void Drive(Firetruck truckToDrive, Point coordinates)
    {
        if (truckToDrive.Driver != this)
        {
            // We can't drive the truck if we're not the driver
            return;
        }
        truckToDrive.Drive(coordinates);
    }
}

class Firefighter : FirefighterBase
{
    public override void ExtinguishFire()
    {
        Console.WriteLine("{0} is putting out the fire!", Name);
        TrainHoseOnFire();
        TurnOnHose();
    }

    protected virtual void TurnOnHose()
    {
        Console.WriteLine("The fire is going out.");
    }

    protected virtual void TrainHoseOnFire()
    {
        Console.WriteLine("Training the hose on the fire.");
    }
}

But we’re still not quite done! If you build this you’ll see another compiler error:

'TraineeFirefighter.TurnOnHose()': no suitable method found to override
'TraineeFirefighter.TrainHoseOnFire()': no suitable method found to override

Our trainee firefighter really is a kind of firefighter, and depends on those two virtual functions we just moved. The error message is telling us that we can’t override a method that isn’t actually present in the base.

We need to change its base class from FirefighterBase to Firefighter. This has the advantage that we can also get rid of its duplicate override of the ExtingushFire method (see Example 4-14).

Example 4-14. Using the newly refactored base classes

class TraineeFirefighter : Firefighter
{
    protected override void TurnOnHose()
    {
        if (hoseTrainedOnFire)
        {
            Console.WriteLine("The fire is going out.");
        }
        else
        {
            Console.WriteLine("There's water going everywhere!");
        }
    }

    private bool hoseTrainedOnFire;
    protected override void TrainHoseOnFire()
    {
        hoseTrainedOnFire = true;
        Console.WriteLine("Training the hose on the fire.");
    }
}

We also need to uncomment our two lines about Joe in the Main function—everything should work again:

Firefighter joe = new Firefighter { Name = "Joe" };
joe.ExtinguishFire();

We can build and run to check that. We get the expected output:

Joe is putting out the fire!
Training the hose on the fire.
The fire is going out.

Bill is putting out the fire!
There's water going everywhere!
Training the hose on the fire.

Let’s remind ourselves of the current class hierarchy (see Figure 4-2). Our FireChief is no longer an “ordinary” Firefighter, with an override for putting out fires, but he does take advantage of our common scaffolding for “firefighters in general” that we modeled as an abstract base class called FirefighterBase. Our Firefighter also takes advantage of that same scaffolding, but our TraineeFirefighter really is a Firefighter—just with its own idiosyncratic way of doing some of the internal methods that Firefighter uses to get the job done.

Back to the requirements for our fire department application: let’s say we want to keep track of who is actually in the fire station at any particular time, just in case there is a fire on the premises and we can take a roll call (health and safety is very important, especially in a fire station).

There are two types of folks in the fire station: the firefighters and the administrators. Example 4-15 shows our new Administrator class.

Example 4-15. A class representing administrative staff

class Administrator
{
    public string Title { get; set; }
    public string Forename { get; set; }
    public string Surname { get; set; }
    public string Name
    {
        get
        {
            StringBuilder name = new StringBuilder();
            AppendWithSpace(name, Title);
            AppendWithSpace(name, Forename);
            AppendWithSpace(name, Surname);
            return name.ToString();
        }
    }

    void AppendWithSpace(StringBuilder builder, string stringToAppend)
    {
        // Don't do anything if the string is empty
        if (string.IsNullOrEmpty(stringToAppend))
        {
            return;
        }

        // Add a space if we've got any text already
        if (builder.Length > 0)
        {
            builder.Append(" ");
        }
        builder.Append(stringToAppend);
    }
}

If you look at our Firefighter class, it had a single string property for a Name. With the Administrator, you can independently get and set the Title, Forename, and Surname. We then provided a special read-only property that returns a single formatted string for the whole Name. It uses a framework class called StringBuilder to assemble the name from the individual components as efficiently as possible.

AppendWithSpace is a utility function that does the actual work of concatenating the substrings. It works out whether it needs to append anything at all using a static method on string that checks whether it is null or empty, called IsNullOrEmpty; finally, it adds an extra space to separate the individual words.

To do the roll call we want to write some code such as that in Example 4-16.

Example 4-16. Using the Administrator class

static void Main(string[] args)
{
    FireStation station = new FireStation();

    // A reference to Joe, Harry's number one
    Firefighter joe = new Firefighter { Name = "Joe" };

    // A reference to Bill, the trainee
    FirefighterBase bill = new TraineeFirefighter { Name = "Bill" };

    // Harry is back
    FireChief bigChiefHarry = new FireChief { Name = "Harry"};

    // And here's our administrator - Arthur
    Administrator arthur = new Administrator
    {
        Title = "Mr",
        Forename = "Arthur",
        Surname = "Askey"
    };

    station.ClockIn(joe);
    station.ClockIn(bill);
    station.ClockIn(bigChiefHarry);
    station.ClockIn(arthur);

    station.RollCall();

    Console.ReadKey();
}

Note

When you are designing a class framework it can often be a good idea to write some example client code. You can then ensure that your design is a good abstract model while supporting clean, simple code at point-of-use.

Clearly, we’re going to need a FireStation class that is going to let our administrators and firefighters ClockIn (registering their presence in the station), and where we can do a RollCall (displaying their names). But what type is that ClockIn function going to take, given that we haven’t specified any common base class that they share?

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required