views:

494

answers:

6

Let's say I have a collection of objects that all inherit from a base class. Something like...

   abstract public class Animal
    {

    }

    public class Dog :Animal
    {

    }

    class Monkey : Animal
    {

    }

Now, we need to feed these animals, but they are not allowed to know how to feed themselves. If they could, the answer would be straightforward:

foreach( Animal a in myAnimals )
{
   a.feed();
}

However, they can't know how to feed themselves, so we want to do something like this:

    class Program
{
    static void Main(string[] args)
    {
        List<Animal> myAnimals = new List<Animal>();

        myAnimals.Add(new Monkey());
        myAnimals.Add(new Dog());

        foreach (Animal a in myAnimals)
        {
            Program.FeedAnimal(a);
        }
    }

    void FeedAnimal(Monkey m) {
        Console.WriteLine("Fed a monkey.");
    }

    void FeedAnimal(Dog d)
    {
        Console.WriteLine("Fed a dog.");
    }

}

Of course, this won't compile, as it would be forcing a downcast.

It feels as if there's a design pattern or some other solution with generics that help me out of this problem, but I haven't put my fingers on it yet.

Suggestions?

+6  A: 

A checked downcast, as used within Linq idioms, is perfectly safe.

foreach (Monkey m in myAnimals.OfType<Monkey>())
    Program.FeedAnimal(m);

Or you could use the visitor pattern. The visitor object knows all the types of animal, so it has a FeedAnimal function for each. You pass the visitor object to an animal's Feed function, and it calls back to the correct FeedAnimal method, passing this.

To make it extensible, you need a Dictionary of animal feeders:

private static Dictionary<Type, Action<Animal>> _feeders;

To register a feeding action, you'd do this to begin with:

_feeders[typeof(Monkey)] = 
    a =>
    {
        Monkey m = (Monkey)a;

        // give food to m somehow
    };

But there's a downcast, and you have to give the correct type in the key as well. So make a helper:

public static void AddFeeder<TAnimal>(Action<TAnimal> feeder) where TAnimal : Animal
{
    _feeders[typeof(TAnimal)] = a => feeder((TAnimal)a);
}

This captures the pattern so you can reuse it completely safely:

AddFeeder<Monkey>(monkey => GiveBananasTo(monkey));
AddFeeder<Dog>(dog => ThrowBiscuitsAt(dog));

Then when you need to feed an animal, use this extension method:

public static void Feed(this Animal a)
{
    _feeders[a.GetType()](a);
}

e.g.

a.Feed();

So _feeders would be a private static field in the same static class as the extension method, along with the AddFeeder method.

Update: all the code in one place, also with support for inheritance:

public static class AnimalFeeding
{
    private static Dictionary<Type, Action<Animal>> _feeders 
        = new Dictionary<Type, Action<Animal>>();

    public static void AddFeeder<TAnimal>(Action<TAnimal> feeder) 
        where TAnimal : Animal
    {
        _feeders[typeof(TAnimal)] = a => feeder((TAnimal)a);
    }

    public static void Feed(this Animal a)
    {
        for (Type t = a.GetType(); t != null; t = t.BaseType)
        {
            Action<Animal> feeder;
            if (_feeders.TryGetValue(t, out feeder))
            {
                feeder(a);
                return;
            }
        }

        throw new SystemException("No feeder found for " + a.GetType());
    }
}

You could also have a loop through the interfaces support by each type t - basically the same idea so I've kept the example simple here.

Daniel Earwicker
(since requested) The `OfType` is fine, but you'd have to have as many `OfType`s as you have breeds - so becomes visitation eventually. The dictionary/delegate-based visitor implementation discussed here is one of many valid approaches, but would need to think a little about inheritance...
Marc Gravell
...i.e. if I registered an action for `Dog`, but add a `Collie` instance, it would break. Also, the lambdas can be simplified (I think) to AddFeeder<Monkey>(GiveBananasTo); AddFeeder<Dog>(ThrowBiscuitsAt). But a reasonable discussion ;-=p
Marc Gravell
The OfType I really mentioned to show how a downcast is fine as long as you encapsulate it to make it safe, as an intro to the rest of the answer.
Daniel Earwicker
Great point about the inheritance - fixed!
Daniel Earwicker
A: 

Since Animals aren't allowed to feed themselves or other animals, you have no other option then to create a class "Owner" or "Keeper" or "Caretaker", who owns a private method to feed animals.

WebDevHobo
+1  A: 

This is a classical problem in OOD. If your set of classes (animals) is fixed, you can use the visitor pattern. If your set of actions (eg. feed) is limited, you just add a method feed() to Animal. If none of this holds, there is no easy solution.

jpalecek
+1  A: 

Generics would only be helpful if you had a "list of dogs" and wanted to call a method with an argument that was "list of things that are animals" (i.e. List<T> where T : Animal) - I don't think it helps here.

I suspect you are going to need a visitor pattern... some set of objects that may know how to feed a given type of animal, and keep trying them until you find one that knows how...

Marc Gravell
Would be interested to hear your comments on my answer, Marc.
Daniel Earwicker
A: 

You could include a member variable in the animal class that would identify the animal's type and then have the feed function read it and produce different results based on it.

Alex Cahill
+2  A: 

First off, one of the main points of object-oriented design is that objects bundle their data with the behavior that acts on that data (i.e., "animals know how to feed themselves"). So this is one of those "Doctor, it hurts when I do this! - So don't do that" situations.

That said, I'm sure there's more to the story than you've described and that you have good reasons for not being able to do "proper" OOD. So you have a few options.

You can have your FeedAnimal(Animal a) method use reflection to find the type of the animal. Basically you're doing your polymorphism in the FeedAnimal method.

static void FeedAnimal(Animal a)
{
    if (a is Dog)
    {
        Console.WriteLine("Fed a dog.");
    }
    else if (a is Monkey)
    {
        Console.WriteLine("Fed a monkey.");
    }
    else
    {
        Console.WriteLine("I don't know how to feed a " + a.GetType().Name + ".");
    }      
}

A more object-oriented, but more complicated way of doing it would be to use the Visitor pattern suggested by others. This is more elegant to the experienced developer, but arguably less obvious and readable to more novice programmers. Which approach you prefer might depend on how many different Animal types you have.

Jesse Smith
You can use 'is': a is Dog, a is Monkey...
strager
Oh, good point - silly of me to forget that.
Jesse Smith
+1 for Visitor pattern.
unforgiven3
@Jesse, it's toxic for a dog to eat too much chocolate, if you want an example of why a dog shouldn't feed itself.
ProfK