| ▲ | bccdee 5 hours ago | |
What I like about the modern¹ approach (interfaces + composition) is that it cleanly untangles polymorphism from behaviour-sharing. When you inherit from a parent class, you have to be careful to only override methods in ways that the parent expects, so the parent's invariants aren't broken². There's a whole additional set of keywords (private/protected/final) meant to express these parent-child contracts. With interfaces + composition, those are unnecessary: You can compose an object and use it however you want; then, if you want the wrapper object to uphold the inner object's contract, you can additionally implement an interface to formalize that. The behaviours you use and the polymorphic guarantees you make are totally separate. Inheritance mixes these ideas together and ends up worse for it. Not only the modern approach is simpler, it's more powerful: (1) Polymorphic extension (i.e. extending a polymorphic parent class at runtime) is doable, and (2) multiple inheritance is a non-issue. [1]: I call it "modern" because newer languages like Go and Rust have eliminated inheritance in favour of exclusively using interfaces/traits. [2]: See the fragile base class problem. | ||
| ▲ | musicale 5 hours ago | parent [-] | |
> modern¹ approach (interfaces + composition) Smalltalk protocols and message categories were a step toward this (for example you could classify messages as implementing a particular interface, such as the collection or stream protocols), but Smalltalk lacked the type and interface checking supported by Java and other languages. | ||