Why do you need to enforce rules, other than documenting the classes/methods defined?
What happens if the superclass method isn't called
Shit doesn't work or it breaks, then the person who sub-classed needs to fix it. Sometimes it also means your class is leaking encapsulation.
This also happens with plain aggregation/composition btw. It also happens with data-immutability (which has nothing to do with inheritance, as your object can be immutable and extend a dozen classes).
Why do you need to enforce rules, other than documenting the classes/methods defined?
The less repetitive work we delegate to human fallibility and instead delegate to a machine, the more time we have for human ingenuity.
Shit doesn't work or it breaks, then the person who sub-classed needs to fix it.
It's not that simple. The more difficult it is to understand the rules of behavior before changing the code, the more difficult it is to change the behavior. It's not just a question of expressing valuable -- but simple -- kindergarden requirements (this value may not be NULL), but higher-level requirements (this method must be called in the context of a READ-COMMITTED transaction).
The more you can express concisely, the easier it is to mutate the system over time. It's not a question of breaking code -- or noticing when it breaks -- but having the language assist in simply not breaking it at all.
This also happens with plain aggregation/composition btw.
Composition makes invariants easier to understand. If you then design your classes so poorly as to fail to enforce correct behavior through their API insofar as it is possible to do so, that is the programmer's failure.
It also happens with data-immutability (which has nothing to do with inheritance, as your object can be immutable and extend a dozen classes).
Data immutability is related to the avoidance of inheritance insofar as they both very significantly facilitate the full and easy comprehension of an implementation invariants.
this method must be called in the context of a
READ-COMMITTED transaction
I get what you're saying, but I like conventions and clear APIs with proper encapsulation.
Here's a sample from Python/Django ...
@transaction.commit_on_success
def do_stuff_with_the_db():
db.execute("insert into tmp values (1)")
raise Exception
Or if you need to supply the DB queries yourself, you can implement your own context-manager than use it with a with block ...
with stuff.inside_transaction() as obj:
obj.execute("query")
No need to extend a class that represents a transaction or some other shit like that.
having the language assist in simply not breaking it at all
You know that's an utopian goal. What I dislike most about languages that try to detect too much shit for me is that it gives me a false sense of security. And the worst offender is Java: not only is its type-system too weak, because it is manifest-typed you get the false impression that it guarantees stuff for you, when it doesn't.
This also happens with plain aggregation/composition btw. It also happens with data-immutability (which has nothing to do with inheritance, as your object can be immutable and extend a dozen classes).