Stop abusing (virtual) animals when teaching programming
How many of you, when reading about tutorial on programming have come across an example like this:
/*
** Inheritance is useful when different classes share functionality. */
using System;class Animal
{
protected string speakSound = "";
public void Speak() {
Console.WriteLine(speakSound);
}
}
class Cat : Animal
{
public Cat() {
this.speakSound = "Meow";
}
}
class Dog : Animal
{
public Dog() {
this.speakSound = "Woof";
}
}
class ZooClass
{
static void Main(string[] args) {
Cat cat = new Cat();
Dog dog = new Dog();
cat.Speak(); // Prints Meow
dog.Speak(); // Prints Woof
}
}
And how many after reading this example think:
“Yeah I get how to use this in a real application”
The example does it’s job on explaining how inheritance works, but offers no clues on what kind of problems you can solve with it and why is it better than an alternative approach to a problem. This is like explaining a hammer as “a useful tool which has a wooden part and a metal part, and hitting something with the metal part makes it go bang”.
Programming patterns and techniques are tools which are used to make it easier to implement complex behavior or logic while keeping the code maintainable and easier to build upon. Most real life examples may be too complex for beginners but I think there exists a middle ground between 1000-line real life examples and “Cow goes moo”.
Teaching using a real example from a real project
Start with a real problem. If possible, in a real project (omitting any details which are not relevant to the problem at hand)
“ Open Source real-time strategy game engine for early Westwood games such as Command & Conquer: Red Alert written in C#…”
In Command & Conquer: Red Alert you take the role of general commanding a part of a warring faction. Your objective is to gather resources, build a base and train an army to crush your opponents.
In this game you can build a unit called Spy which can infiltrate enemy buildings and, depending on the building being infiltrated, can cause different kinds of effects.
Some examples include (from CNC Wiki):
Airfield Displays currently produced unit when the building is selected
Displays amount of ammunition on currently landed aircraftPower plant Displays a five-box vertical indicator of enemy power status
Ore refinery Displays capacity indicator and amount of presently held ore
Now here is the problem: How should I implement the different effects of infiltration?
// Not like this
if (building.name == "Airfield") { airfieldInfiltration(); }
if (building.name == "Power Plant") { powerPlantInfiltration(); }
if (building.name == "Ore Refinery" { oreRefineryInfiltration(); }
Most of the buildings cannot be infiltrated, so creating a base class doesn’t seem right, but all of the buildings have wildly different actions so lumping everything into one class doesn’t seem right either.
After the reader understands the problem, then you present the solution: Interfaces
OpenRA uses an interface called INotifyInfiltrated (declared here) which is used to implement the different functionality for when a spy enters an enemy building. The interface is implemented for any buildings which can be infiltrated, and the effect behavior is found inside each of the different buildings classes, instead of a base class or some huge blob of if-else statements. (An example of stealing cash using the interface can be found here. Note that the behaviors are wrapped in their own classes because the buildings are not hard coded so it doesn’t strictly follow the example provided, but there is no reason it couldn’t).
A code example would look like this. It is not self contained and wont compile, but I think forcing it to be removes the usefulness. Real problems don’t exist in a bubble.
public interface INotifyInfiltrated {
void Infiltrated(Player playerInfiltrating);
}
class Airfield : INotifyInfiltrated
{
public void Infiltrated(Player playerInfiltrating) {
// Displays currently produced unit when
// the building is selected
Unit producingUnit = this.GetCurrentlyProducingUnit()
producingUnit.revealToPlayer(playerInfiltrating);
// Displays amount of ammunition on
// currently landed aircraft
Unit landedUnit = this.GetCurrentlyLandedUnit();
landedUnit.revealStatsToPlayer(playerInfiltrating);
}
}
class PowerPlant : INotifyInfiltrated
{
public void Infiltrated(Player playerInfiltrating) {
// Displays a five-box vertical indicator
// of enemy power status
this.revealPowerStatusToPlayer(playerInfiltrating);
}
}
class OreRefinery : INotifyInfiltrated
{
public void Infiltrated(Player playerInfiltrating) {
// Displays capacity indicator
//and amount of presently held ore
this.revealOreStatusToPlayer(playerInfiltrating);
}
}
Now the reader has seen an actual example of how an interface is used to solve a real-life programming problem. They can apply this whenever they come across a similar sort of problem where needed behavior is “similar for some, but not for all”.
So after reading this maybe next time someone asks about interfaces, you don’t have to drag out poor virtual cats and dogs to bark and meow make your point.