Learn when to use inheritance and composition. Inheritance may sound like great implementation of object oriented programming, something that saves a lot of code. But it has risks. If used wrong it easily leads to tight coupling – hard to maintain code.
Deep inheritance trees may result in execution of unnecessary code and lose of original class intention down the way. Refactoring such structures can also be risky and sometimes vary hard. Why?
Derived classes may extend the responsiblity of the base class to the level of breaking the single resposibility principle. Moreover the responsibility may now be split between to classes. Which one should be modified to fix this? How will it affect the system?
Changes to the base class may change behaviour of derived classes which increases complexity. This is the thing for composition as well, but usually the responsibility of both modules is better defined.
In many cases the same result can be achieved with composition which is far more flexible and enables loose coupling easier.