Simple simulation of animals eating other animals I was given the exercise of refactoring the following code, thus ensuring it was following the SOLID design principles. It refers to an Animal Park, where there are a couple of different species of animals, and it defines the rules in which these animals can eat other animals. The Animal Park class processes a polymorphic array of the represented animals. 

    public abstract class Animal
    {
        public Size Size { get; set; }
        public bool IsDead { get; set; }

        public Animal(Size size)
        {
            this.Size = size;
            this.IsDead = false;
        }

        public abstract void Eat(Animal obj);
    }

    public enum Size
    {
        Small,
        Medium,
        Big
    };

    public class Lion : Animal
    {
        public Lion(Size size) : base(size)
        {
        }

        public override void Eat(Animal animal)
        {
            if (animal is Lion &amp;&amp; this.Size &gt; animal.Size)
            {
                animal.IsDead = true;
            }

            if (animal is Hyena &amp;&amp; this.Size &gt;= animal.Size)
            {
                animal.IsDead = true;
            }
        }
    }

    public class Hyena : Animal
    {
        public Hyena(Size size) : base(size)
        {
        }

        public override void Eat(Animal animal)
        {
            if (animal is Hyena &amp;&amp; this.Size &gt; animal.Size)
            {
                animal.IsDead = true;
            }
        }
    }

    public static class AnimalPark
    {
        public static void Process(IList&lt;Animal&gt; animals)
        {
            int length = animals.Count;
            for (int i = 1; i &lt; length; i++)
            {
                animals[i - 1].Eat(animals[i]);
            }
        }
    }


The code above breaks the Open/Closed Principle because every time I would need to add a new animal, I would not be able to do it just by extending the existing classes or by adding new ones: I would have to modify the existing classes. But I am struggling a lot to fix it properly. I came up with a solution (below), but not only I rely on Reflection, which I think can be avoided, it won't work in the cases where the Eat() method is not implemented, thus ending calling the Eat() method in the base class in a loop.

public interface IEater
{
    public void Eat(IEater obj);
}

public abstract class Animal : IEater
{
    public Size Size { get; set; }
    public bool IsDead { get; set; }

    public Animal(Size size)
    {
        this.Size = size;
        this.IsDead = false;
    }

    public void Eat(IEater obj)
    {
        if ( this.IsDead )
        {
            return;
        }
        var method = this.GetType().GetMethod("Eat", new Type[] { obj.GetType() });
        method.Invoke(this, new object[] { obj });
    }
}

public enum Size {
    Small,
    Medium,
    Big 
};

public class Lion : Animal
{
    public Lion(Size size) : base(size)
    {
    }

    public void Eat(Hyena hyena)
    {
        if (this.Size &gt;= hyena.Size)
        {
            hyena.IsDead = true;
        }
    }

    public void Eat(Lion lion)
    {
        if (this.Size &gt; lion.Size)
        {
            lion.IsDead = true;
        }
    }
}

public class Hyena : Animal
{
    public Hyena(Size size) : base(size)
    {
    }

    public void Eat(Hyena hyena)
    {
        if (this.Size &gt; hyena.Size)
        {
            hyena.IsDead = true;
        }
    }
}

public static class AnimalPark
{
    public static void Process(IList&lt;IEater&gt; animals)
    {
        int length = animals.Count;
        for(int i=1; i&lt;length; i++)
        {
            animals[i - 1].Eat(animals[i]);
        }
    }
}


What are your thoughts on my solution? How would you handle this case?
every time I would need to add a new animal, I would not be able to do it just by extending the existing classes or by adding new ones: I would have to modify the existing classes.


The problem exists in both your approaches. It is because the eating preferences of individual species is mutualy dependent on all other existing animals. If you add new animal, you define what existing animals it likes to eat, but unless you modify existing animals, no existing animal will ever be interested in eating the new animal.

Generally relying on object being of a specific type is not polymorphism, it's a rape.

You need to abstract and centralize the eating preferences somehow.

public enum AnimalKind
{
  Lion,
  Hyena
}

public interface IDinner
{
  public AnimalKind Kind {get;}
  public Size Size {get;}
}

public interface IEater
{
  public bool Likes(IDinner dinner); 
}

public interface IAnimal : IEater, IDinner
{
  bool IsDead {get;}
  void Kill();
}

class Animal : IAnimal
{
  public AnimalKind Kind {get;}
  public Size Size {get;}
  public bool IsDead {get;}

  private IDictionary&lt;AnimalKind, Size&gt; Preferences {get;}

  public Animal(AnimalKind kind, Size size, IDictionary&lt;AnimalKind, Size&gt; preferences)
  {
    this.Kind = kind;
    this.Size = size;
    this.Preferences = preferences;
  }

  public bool Likes(IDinner dinner)
  {
     var maxSize = this.Preferences[dinner.Kind];
     return maxSize != null &amp;&amp; dinner.Size &lt;= maxSize;
  }

  public void Kill()
  {
    IsDead = true;
  }
}

public interface IAnimalFactory
{
  public IAnimal CreateAnimal(AnimalKind kind, Size size);
}

public class AnimalFactory : IAnimalFactory
{
  // centralized place for all animals and their eating preferences
}

public static class AnimalPark
{
    public static void Process(IList&lt;IAnimal&gt; animals)
    {
        int length = animals.Count;
        for(int i=1; i&lt;length; i++)
        {
            var eater = animals[i - 1];
            var dinner = animals[i];
            if (!dinner.IsDead &amp;&amp; !eater.IsDead &amp;&amp; eater.Likes(dinner)) {
                dinner.Kill();
            }
        }
    }
}


Adding new animal now means just extending AnimalKind enum and modifying AnimalFactory class to be aware of the new kind.

Although you might object that having the enum is also not nice. And I kinda agree, but I didn't want to invent an abstraction of the dinner description that was not provided by you. Maybe dinner description could instead be something like a description of how big teeth and paws the dinner has, how aggresive and vicious they look, if they have colors that imply being venomous, etc... If you impleent something like this and get rid of the AnimalKind enum, you will need to change the factory to contain a method per existing animal, but I would not consider it a problem, it may actually be so even with the enum existing...

Also notice I have moved the actual killing of the animal to the AnimalPark class, because that would be the same for all animals. An animal is always killed when eaten and stays alive when not eaten, right?