Composition over inheritance
[A] Composition over inheritance is generally a good rule to follow,
[B] but there are some cases where inheritance is a must
Your conclusion in B implies that you are understanding A to mean "composition should always be used instead of inheritance". This interpretation is not correct.
There are some cases where inheritance is the only reasonable working solution. There are some cases where composition is the only reasonable working solution. Neither of these cases are relevant when considering "composition over inheritance".
There are some cases where composition and inheritance are both reasonable working solutions. "Composition over inheritance" advises that in these specific cases where either would a viable solution, that you should favor composition because it's not as likely for you to end up having painted yourself in a corner later on in the development cycle.
The example you bring to the table is one where inheritance is the only reasonable workable solution, and is therefore irrelevant as to the "composition over inheritance" guideline.
Similarly, the hypothetical you present is one where composition is the only reasonable workable solution, and is therefore equally irrelevant as to the "composition over inheritance" guideline.
The core of the question is irrespective of composition over inheritance. You're essentially asking "how to do A, which only works in situation X, while at the same time doing B, which only works in situation !X". By definition, you can't.
The question is: are there any existing strategies to achieve this behavior via composition that do not involve things like external maps or pointer hackery?
You already pre-empted the response, but in cases where only inheritance makes sense and not composition, just use inheritance.
Using the wrong solution for your scenario means that any difficulties you encounter from doing so are a self-imposed hurdle, and trying to make it work without addressing that hurdle turns into an XY problem really quickly.
Of course the obvious answer is "just use inheritance", but if the Node class is made by a factory or is otherwise inconvenient to inherit from, what are the alternatives? Obviously a map is one option, but that is very inconvenient and messy, and not to mention slow.
If the Node
class is maintained by you, then this is another self-imposed hurdle. The obvious solution is to adapt the Node
implementation so that it is inheritance-friendly.
If the Node
class is maintained by the library developer, the same advice applies as it does for any complaint about the library you're using: either find another library to use, get the library developer to change their library, or deal with it. That last option then also entails letting go of the fact that it's going to require a dirty hack to get it working the way you need it to.
If the library designer chose to make it impossible to derive the Node
class, or unknowingly designed their library in a way that makes it impossible, then that's how the library is designed. Any circumvention of that design is therefore by definition a hack.
An informal way to define a hack is that it is an inferior/shoddy solution when a better solution is available. The corollary here is that if there are no better solutions to the problem, then it's not a hack.
? Like, this is solvable without inheritance if the graph library takes an
interface Node` instead of a concrete base class one must inherit from.GraphUtil
about that, maybe they had a good reason. My only question was what to do if they didn't anticipate this use case, or could not accommodate it for whatever reason.