I usually think of the ideas behind "composition" as "how do I assist a future developer to replace the current (exported) implementation of a type with a new one by restricting external visibility of its internal implementation through the use of private methods and data".
In "inheritance", it often feels like the programmer's mindset is static, along the lines of "here is a deep relationship that I discovered between 2 seemingly unrelated types", which ends up being frozen in time. For example, a later developer might want to make a subtle innovation to the base type; it can be quite frightening to see how this flows though the "derived" types without any explicit indiction.
Of course, YMMV, but I think of "composition" as "support change" and "inheritance" as "we found the 'correct way to think about this' and changes can be quite difficult".
Since I think that the key to building large systems handling complex requirements is 'how do we support disciplined change in the future' (empowering intellectual contributions by later generations of developers rather than just drudge maintenance).
In "inheritance", it often feels like the programmer's mindset is static, along the lines of "here is a deep relationship that I discovered between 2 seemingly unrelated types", which ends up being frozen in time. For example, a later developer might want to make a subtle innovation to the base type; it can be quite frightening to see how this flows though the "derived" types without any explicit indiction.
Of course, YMMV, but I think of "composition" as "support change" and "inheritance" as "we found the 'correct way to think about this' and changes can be quite difficult".
Since I think that the key to building large systems handling complex requirements is 'how do we support disciplined change in the future' (empowering intellectual contributions by later generations of developers rather than just drudge maintenance).