When I was introduced to object-oriented programming, inheritance was one of the main principles taught. Inheritance is a good concept to learn. There are type relationships where something “is-a” child type of an existing parent type.
However, composition is a much more useful concept. When something “has-a” typed member, it is a more useful relationship.
For instance, instead of caring that types inherits from an
Animal base implementation, the concern should be that individual
types implement a set of methods (hence implementing an
interface). For types which are composed with other member
types, you can implement the interface by adapting to the
interface and delegating to a composed member instance.
Adapting to an interface (whether strongly defined or just by “duck typing”) using delegation gains the benefits of re-using a method implementation without the downsides of inheritance.
The downsides of inheritance include (in most languages) only allowing single parent type inheritance, base parent types which cannot be easily changed without affecting child types, and potential complexities in multiple layers in inheritance type hierarchies.
Inheritance comes with many limitations and a few benefits, but composition is ultimately what should be preferred.