The simplest way to solve this if every Animal knows itself what they are supposed to be fed. So:

interface Animal {
Food wantedFood();
}

This might completely solve your problem, and if so, is a good solution.

But while this is simple, this is often unsatisfactory: your Animal interface will grow and grow with marginally related methods. Or maybe we don't want each animal to feed itself, and rather want a separate ZooKeeper to handle this. A ZooKeeper knows how to handle() each Animal. How does the ZooKeeper know which animal they are handling? There's a technique for this called double dispatchdouble dispatch, where the Animal is responsible for asking the ZooKeeper for correct handling. This looks very much like the above example where the Animal itself knows what food we want, but once we have a generic ZooKeeper we can have different ZooKeepers with different behaviours:

This is generally known as the visitor patternvisitor pattern, and allows us to implement a function differently for different types, even if we don't know which kind of type we'll get at run time. However, there is a large trade off here: if the zoo gets a new Animal, we have to update the ZooKeeper interface, and all ZooKeeper implementations with it. This is bad, because this makes it hard to extend, but also good because it makes it more difficult to forget to handle a case.

There are two possible approaches here.

The simplest way to solve this if every Animal knows itself what they are supposed to be fed. So:

interface Animal {
Food wantedFood();
}

This might completely solve your problem, and if so, is a good solution.

But while this is simple, this is often unsatisfactory: your Animal interface will grow and grow with marginally related methods. Or maybe we don't want each animal to feed itself, and rather want a separate ZooKeeper to handle this. A ZooKeeper knows how to handle() each Animal. How does the ZooKeeper know which animal they are handling? There's a technique for this called double dispatch, where the Animal is responsible for asking the ZooKeeper for correct handling. This looks very much like the above example where the Animal itself knows what food we want, but once we have a generic ZooKeeper we can have different ZooKeepers with different behaviours:

This is generally known as the visitor pattern, and allows us to implement a function differently for different types, even if we don't know which kind of type we'll get at run time. However, there is a large trade off here: if the zoo gets a new Animal, we have to update the ZooKeeper interface, and all ZooKeeper implementations with it. This is bad, because this makes it hard to extend, but also good because it makes it more difficult to forget to handle a case.

There are two possible approaches here.

The simplest way to solve this if every Animal knows itself what they are supposed to be fed. So:

interface Animal {
Food wantedFood();
}

This might completely solve your problem, and if so, is a good solution.

But while this is simple, this is often unsatisfactory: your Animal interface will grow and grow with marginally related methods. Or maybe we don't want each animal to feed itself, and rather want a separate ZooKeeper to handle this. A ZooKeeper knows how to handle() each Animal. How does the ZooKeeper know which animal they are handling? There's a technique for this called double dispatch, where the Animal is responsible for asking the ZooKeeper for correct handling. This looks very much like the above example where the Animal itself knows what food we want, but once we have a generic ZooKeeper we can have different ZooKeepers with different behaviours:

This is generally known as the visitor pattern, and allows us to implement a function differently for different types, even if we don't know which kind of type we'll get at run time. However, there is a large trade off here: if the zoo gets a new Animal, we have to update the ZooKeeper interface, and all ZooKeeper implementations with it. This is bad, because this makes it hard to extend, but also good because it makes it more difficult to forget to handle a case.

The simplest way to solve this if every Animal knows itself what they are supposed to be fed. So:

interface Animal {
Food wantedFood();
}

This might completely solve your problem, and if so, is a good solution.

But while this is simple, this is often unsatisfactory: your Animal interface will grow and grow with marginally related methods. Or maybe we don't want each animal to feed itself, and rather want a separate ZooKeeper to handle this. A ZooKeeper knows how to handle() each Animal. How does the ZooKeeper know which animal they are handling? There's a technique for this called double dispatch, where the Animal is responsible for asking the ZooKeeper for correct handling. This looks very much like the above example where the Animal itself knows what food we want, but once we have a generic ZooKeeper we can have different ZooKeepers with different behaviours:

This is generally known as the visitor pattern, and allows us to implement a function differently for different types, even if we don't know which kind of type we'll get at run time. However, there is a large trade off here: if the zoo gets a new Animal, we have to update the ZooKeeper interface, and all ZooKeeper implementations with it. This is bad, because this makes it hard to extend, but also good because it makes it more difficult to forget to handle a case.