Здесь есть две проблемы, которые часто путают. Унаследованное поведение позволяет одинаково реагировать на различные «команды», например, Man.walk () === Woman.walk (). Полиморфное поведение позволяет реагировать на одну и ту же «команду» по-разному, например, Animal.move () для одного объекта может отличаться для Animal.move () для другого, птица будет летать, а слизняк будет скользить.
Теперь я бы сказал, что второе из них хорошо, а первое - нет. Зачем? Потому что в ООП мы должны инкапсулировать функциональность в объекты, что, в свою очередь, способствует повторному использованию кода и всех других преимуществ ООП. Поэтому вместо того, чтобы наследовать поведение, мы должны делегировать его общему объекту. Если вы знаете закономерности, то этим занимаются государство и стратегия.
Проблема заключается в том, что обычно, когда вы наследуете, оба эти поведения смешиваются вместе. Я полагаю, что это больше проблем, чем стоит, и мы должны использовать только интерфейсы, хотя иногда нам приходится обходиться тем, что предоставляет инфраструктура.
В вашем конкретном примере Mammal - это, вероятно, плохое имя интерфейса, потому что оно на самом деле не говорит мне о том, что он делает, и потенциально может распространиться на тысячи методов. Лучше разделить интерфейсы на очень конкретные случаи. Если вы моделировали животных, у вас мог бы быть интерфейс Moveable с одним методом, move (), на который каждое животное могло бы реагировать, идя, бегая, летая или ползая в зависимости от ситуации.