I think for the usecase you mention, there is a different solution that I personally prefer.
Optimally, if your language supports it, just define an interface with these 5 methods and then define extension methods that work on any type that implements the interface. The reason why this works is that all the other functions are usually helper functions / convenience functions and they only need the other 5 functions to work, so no need to access any inner/private properties.
That is by far the most lightweight solution, and it works even for 3rd-party libraries where you can't control the types.
If your language does not have extension methods, then you can still use composition just like in the example I gave and delegate to the base class. That is a bit more code to write (because you have to delegate to the non-5 methods if you language doesn't automate that for you, some do) but I find it cleaner than hoping that no one overwrites any non-5 method.
But yeah, essentially what you are saying here is to ask people to follow the rule I gave by convention - depending on that context that might work as well.
Yeah, I generally like composition better, but sometimes the coupling between the base class and those subclasses that provide the 5 methods is just too strong to ignore, and if you break out the methods into another interface, then you are struggling to find a place to put the shared logic (which you proposed to do with extension methods).
Your example with the counting stack made use of "super", which is always a red flag to me. "super" is such a bad smell to me, that I literally never use it. In fact, in Virgil, my language project (http://github.com/titzer/virgil), there is no super construct at all, nor static methods or interfaces for that matter. 15 years of writing it, 200k lines later, and I can say that personally, having delegates, first-class functions, partial application, and tuples go way further than more complex trait/interface/extension method madness.
Not sure if I understand you correctly here. With "the coupling ... is just too strong to ignore" you mean that if someone has one of the subclasses at hand, they should automatically have all the extension methods at hand not having to look for them somewhere?
> Your example with the counting stack made use of "super", which is always a red flag to me. "super" is such a bad smell to me, that I literally never use it.
I think that's a sign that you have developed a good intuition of that it can lead to problems! :)
Optimally, if your language supports it, just define an interface with these 5 methods and then define extension methods that work on any type that implements the interface. The reason why this works is that all the other functions are usually helper functions / convenience functions and they only need the other 5 functions to work, so no need to access any inner/private properties.
That is by far the most lightweight solution, and it works even for 3rd-party libraries where you can't control the types.
If your language does not have extension methods, then you can still use composition just like in the example I gave and delegate to the base class. That is a bit more code to write (because you have to delegate to the non-5 methods if you language doesn't automate that for you, some do) but I find it cleaner than hoping that no one overwrites any non-5 method.
But yeah, essentially what you are saying here is to ask people to follow the rule I gave by convention - depending on that context that might work as well.