3 ms·
I would argue that much like Go's aversion to deep inheritance hierarchies, this interface hierarchy would be annoying to use in practice. If A, B, and C are d
by CoolGuySteve 7y ago
I would argue that much like Go's aversion to deep inheritance hierarchies, this interface hierarchy would be annoying to use in practice.
If A, B, and C are different families of functionality then ditch the inheritance and use separate interfaces for each family of functionality.
The cascading dynamic_cast logic required is a code smell that this could be made simpler.
- cryptonector 7y ago> this interface hierarchy would be annoying to use in practice No doubt, but it happens, and sometimes because the refactoring you might prefer to do can't be done or can't be done right away. For example, in Java you might have a subclass of Principal where you store additional metadata and have additional methods, then you might have code like: public static Something princOk(Principal p, Stuff stuff) throws MyPrincipalException { if (p instanceof MyPrincipal) return princOk((MyPrincipal)p, stuff); return princOk(MyPrincipal(p), stuff); } Why would you do this? Well... because many JDK and third-party interfaces take Principal object arguments that you could reuse... if only you could decorate those objects with the additional metadata that you wanted, and... with subclassing you get to. In Java you might say "use Subject", but it's not that easy, and anyways, JAAS is a terrible terrible thing that should have been ripped out when applets were ripped out.
- CoolGuySteve 7y agoIn C++, you can use templates to pattern match/duck type interfaces at compile time. So "template<typename PrincType> void princOk(PrincType& p, Stuff& stuff) {p.doThing(stuff); }" would map properly as long as doThing was implemented in all the different classes. You don't actually need an interface or base class for it to work. The rest of the dynamic mapping is faster by calling switch() on an enum class member and than static_cast rather than relying on dynamic_cast.
- MauranKilom 7y agoIt actually works really well in practice for the example I'm thinking of (and this is deep in the bones of a code base, not just something I thought of for fun). Each functionality is a true subset of the other. It's not different families of functionalities. Also, I don't see how your suggestion would help with void foo(const AInterface& someObject); What type should foo take instead if it can handle all levels of guarantee but might engage better code paths the more guarantees are available? Would you use a sum type (which in C++ boils down to type erasure, i.e. polymorphy but hidden and costlier)? Would you overload it (and then have code duplication)? I'm not convinced you could make it simpler.
- CoolGuySteve 7y ago"template<typename ABCType> foo(const ABCType& someObject)" will call the most optimized code path at compile time if the type is properly cast before the call. In this case, the inheritance is unnecessary as long as all the types implement the same function names. So the runtime type detection, whatever it is, only needs to be run once at the start of the callstack and then an entire chain of operations will be more optimized than if you had used interfaces and virtual functions throughout.
- MauranKilom 7y ago...and every single piece of code in your entire code base that touches these classes (and that's like 80% of the higher level code in this particular case) would have to be templated. So either everything has to be in headers, or you need 3 (well, 5 in reality, I abbreviated the example) explicit template instantiation declarations per function in the header and as many explicit template instantiation definitions in the source file. Either way lies madness.