Specific-Generic Pattern

May 6, 2014 - Programming

Sometimes when dealing with families of classes you want a method which can be completely generic when dealing with collections of the family and specific when dealing with a specific class.

In this example we’re going to look at the (very simplified) food chain as an example – rabbits eat grass, foxes eat rabbits, badgers eat foxes (had to Google that one to be honest).

We want to be able to allow a fox to eat a rabbit fox.Eat(rabbit) but would like an error to be thrown (in the example I am returning false, but an ArgumentException is what I’m using in my real-world implementation of this) if a fox tries to eat anything else – fox.Eat(fox) // throw new CanabalismException.

We achieve the general case of consuming any IEdible with an abstract base class:

public abstract class Animal : IEdible
{
    public abstract bool Eat(IEdible food);
}

and we specialise with a generic abstract class inheriting from the above

public abstract class Animal<TFood> : Animal where TFood : IEdible
{
    public abstract bool Eat(TFood food);

    public override bool Eat(IEdible food)
    {
        if (!(food is TFood))
            return false; // perhaps throw ArgumentException in real life

        var cast = (TFood)food;

        return this.Eat(cast); // call the override which is defined in the derived class
    }
}

Here we implement the base class’s Eat method which calls an override within this class, to be defined in derived classes. What this means is that we can remove duplication of guard clauses which we would require if we were to only have abstract class Animal. This also gives us the opportunity to perform any shared operations at this stage whilst keeping DRY.

Now let’s make some animals:

public class Rabbit : Animal<Grass>
// or public class Fox : Animal<Rabbit>
// or public class Badger : Animal<Fox>
{
    public override bool Eat(Grass grass)
    // or public override bool Eat(Rabbit rabbit)
    // or public override bool Eat(Fox fox)
    {
        // eat some grass or rabbit or fox
        return true;
    }
}

I’ve omitted the definition of Grass and IEdible for brevity.

Finally let the feast begin:

public void FoodChain()
{
    // set up our food chain
    var array = new Animal[] { new Rabbit(), new Fox(), new Badger() };

    // find all combos of non-cannibalism 
    var combos = from a1 in array
                 from a2 in array
                 where a1 != a2
                 select new { Predator = a1, Prey = a2 };

    foreach (var combo in combos)
    {
        string message;

        if (combo.Predator.Eat(combo.Prey))
            message = "The {0} ate a {1}";
        else
            message = "{0}s do not eat {1}s";

        Console.WriteLine(message, combo.Predator, combo.Prey);
    }
}

Output (with poor grammars):

Rabbits do not eat Foxs  
Rabbits do not eat Badgers
The Fox ate a Rabbit
Foxs do not eat Badgers
Badgers do not eat Rabbits
The Badger ate a Fox

This pattern is quite flexible – you can even have your return type as an interface in the base class with a concrete implementation in the generic class. This means that you can deal with families of classes in bulk whilst still getting type-safety when dealing with a specific implementation:

// type-safe happyness
Rabbit baby = mummyRabbit.Copulate(daddyRabbit);

// abstract easiness
foreach (var couple in couples)
{
    Animal baby = couple.Mummy.Copulate(couple.Daddy)
}

› tags: c# /