How to change this design to avoid demotion?

Say I have a set of objects that all inherit from a base class. Sort of...

   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 simple:

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

However, they cannot 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 will not compile, as this will lead to concealment.

It seems like there is a design template or some other solution with generics that help me solve this problem, but I haven't put my fingers on it yet.

Suggestions?

+3
source share
6 answers

downcast, Linq, .

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

. - , FeedAnimal. Feed, FeedAnimal, this.

, Dictionary :

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

, , :

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

        // give food to m somehow
    };

, . , :

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

, :

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

, , :

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

.

a.Feed();

, _feeders , , AddFeeder.

: , :

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());
    }
}

t - , .

+7

, - , , (.. " , " ). ", , !" ".

, , , , "" OOD. , .

FeedAnimal (Animal a) . FeedAnimal.

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 + ".");
    }      
}

-, , . , , , . , , .

+2

OOD. () , . (, ) , () Animal. , .

+1

, " " , " , " (.. List<T> where T : Animal) - , .

, ... , , , , , , ...

+1

, , "" "" "", .

0

You can include a member variable in the animal class that would identify the type of animal, then read the feed function and produce different results on it.

0
source

Source: https://habr.com/ru/post/1705142/


All Articles