It can also produce code that is not easily and clearly extendable, if the behavior you're trying to extend is buried a layer or two further down the inheritance hierarchy.
Unfortunately, due to the First Law of Kipple, the rate at which you run into this problem is proportional to the age of the code base. And so we grow frustrated with implementation inheritance. Other extensibility mechanisms probably have similar pitfalls, but they haven't been the dominant way of doing things for long enough to accumulate the same volume of clutter.
Don Knuth has mentioned in a few interviews that he isn't so hot on code reuse, and prefers code that's designed to be easy to edit over code that's designed to be easy to extend. I'm starting to see some wisdom in that idea. With the one, being able to keep things tidy is a primary goal. With the other, eventually tidying becomes a frightening enterprise, because you have to avoid upsetting the inheritance hierarchies that are precariously balanced on top of the code you're trying to tidy up.
I can't say that I know a software development idiom that reliably creates a more stable equilibrium point. But I don't think we'll ever find one unless we're willing to examine the failure modes of existing paradigms.
It's hard to define a "good practice" for this wildly heterogenous group. What's good for a beginner (training wheels on a child's bike), is completely counter-productive to a pro (motorcycle sports driver).
Not sure I understood your example about the static class vs. function factory tbh though.
class ServiceWrapper:
service = ...
v1 = ...
v2 = ...
@static
def my_job():
return service.call(v1, v2)
vs.def create_service_wrapper(v1=..., v2=..., service=...):
def f():
return service.call(v1, v2);
return f
The class can scale to multiple methods sharing parameters, but the semantics of the factory fall apart if you want to return more than one parameterized function.Java doesn't let you have inheritance without polymorphism, but it is possible, see "private inheritance" in C++.
This is because Java has single-inheritance enforcement for classes.
C++ for example has multiple inheritance. So the way you do an interface is you just write an abstract class, then extend it to implement it.