- don't use interfaces until you absolutely have to, and then try and find another way.
YMMV, but in my corporate experience with OO code, I find a lot of interfaces that have one implementation. It's a waste to plan for a future that doesn't come.
I feel we're lacking something like software mindfulness. Where you stay in the moment and just solve the problem.
I still stick to two levels of inheritance max though, and most of the time I only use interface inheritance. Rarely do I want behavior inherited.
I also love YAGNI - You Ain't Gonna Need It!
Once you get at that level, objects are life savers. Because then state is explicit on code rather then hidden in deep call stack and spread over various levels of it.
There are people who think they need a singleton all the time, imagine where similar definition would have brought them.
How a function is defined varies from language to language, but if we're talking about a globally accessible named function, this is in effect a singleton.
It means you can't configure this function (through construction parameters), or replace it, or mock it, etc. it's in effect a "singleton function".
BTW, abstraction != inheritance.
- When all you need is a function, don't make an object abstraction.
- Restrict your abstractions to at most 2 levels of depth (a base class and derived). It becomes more intimidating to figure how the flow goes if you have deep object hierarchies. This also helps to encourage forms of composition.
- Again, don't make an abstractions that provide little to no value to suit some orthodoxy or perceived 'best practice'.