And stop monkey-patching.

High-level modules should not depend on low-level modules. Both should depend on abstractions. Also, abstractions shouldn’t depend on details. Details should depend on abstractions. These abstract interfaces become the contract on how two modules will communicate. Each module can change internally, but as long as they implement the abstract interface, they will still work together.
No class should derive from a concrete class. If you derive from a concrete class, you become more dependent on specific implementations.
No method should override an implemented method. This is a sure-fire way to find surprises further down the line. People expect one behavior, and then you’ve gone and changed the behavior. Bugs will follow.
Variable instantiation should use factories or a Dependency Injection framework.
Reducing dependencies and relying on abstracts leads to code that is easier to test, simpler to change, and more straightforward to support.