Is it bad, how to fix, interface hierarchy.



  • So, I'm running into a problem in the code, where I'm trying to introduce new things, however, I can't get them to work with existing things because of the way we do interfaces.

    For example.

    I have class A and B derives from A. Then we make interfaces iA and iB, but iB derives from iA. This leads to a problem with my new stuff, because I need the existing code to recognize and treat my new class as iB, but I won't have any iA functionality. So have to mock out iA to be no op, but that starts down an endless rabbit hole of mess.

    Now, this has got to be an anti-pattern. It's one thing to say "We need iA and iB for DI reasons." It's another to derive iB from iA and create a dependency between the interfaces.

    Is there anything out there describing this as an anti-pattern and has material to show how to properly implement interfaces to avoid this?



  • @xaade Now, I might be wrong here but isn't the whole point of inheritance that derivative classes inherit the (at least superficially, regardless of whatever overridden stuff actually lurks under the hood) functionality of their parent class?

    To me this sounds more like you need two separate interfaces.


  • And then the murders began.

    @xaade Look for the phrase “composition over inheritance”.

    It’s okay to have one class that implements both IA and IB, but the interfaces should be distinct.



  • @Rhywden said in Is it bad, how to fix, interface hierarchy.:

    @xaade Now, I might be wrong here but isn't the whole point of inheritance that derivative classes inherit the (at least superficially, regardless of whatever overridden stuff actually lurks under the hood) functionality of their parent class?

    To me this sounds more like you need two separate interfaces.

    I'll get a more specific example.

    ErrorForm : Form, IErrorForm

    IErrorForm : IForm

    Now if I want the functionality of IErrorForm for a new class, I can't do it without using Form functionality. If I have NewForm, I have to use the IForm interface.



  • @Unperverted-Vixen said in Is it bad, how to fix, interface hierarchy.:

    “composition over inheritance”.

    I figured this was the case. I just wanted to make sure.

    The only pet peeve I have with CoI, is that if I have a composite class, that derives from the various interfaces to expose that functionality, I have to create the slew of methods just to forward the calls to the contained classes.

    I wish there was a way to automate that, for methods that don't need to communicate between the contained classes.



  • @xaade Then you have to separate out what you want. Everything that's shared goes into one interface, everything that isn't goes into another one.

    Thus:

    class A : ICommon, IA
    
    class B : ICommon, IB
    

  • Trolleybus Mechanic

    @Unperverted-Vixen said in Is it bad, how to fix, interface hierarchy.:

    @xaade Look for the phrase “composition over inheritance”.

    It’s okay to have one class that implements both IA and IB, but the interfaces should be distinct.

    Yeah, split them up. You can even draw a wall around the existing IB that assumes IA by making an empty IAB that just inherits both and change existing types to use IAB. Probably a giant pain but then you'd be well set up to avoid perpetuating the existing mess and you can slowly break the older code up over time.

    If you want to prevent new usages of IAB, in C# anyway, you could mark IAB as deprecated, enable "warnings are errors", and put "ignore warning" markers on all the legacy usages of IAB.



  • @xaade Inheritance is good for exactly one thing: implementing the template pattern (i.e., implementing algorithms generically, while allowing the sub-steps to vary).

    So don't do it unless you're in that situation. Arguably, a form-like object is in that situation.



  • @Rhywden said in Is it bad, how to fix, interface hierarchy.:

    @xaade Then you have to separate out what you want. Everything that's shared goes into one interface, everything that isn't goes into another one.

    Thus:

    class A implements ICommon, IA
    
    class B implements ICommon, IB
    

    Yes. I want to do this.

    The problem is that they've created far too many dependencies between IB and IA.

    If you have an IB, they've assumed it's an IA.



  • @mikehurley said in Is it bad, how to fix, interface hierarchy.:

    Yeah, split them up. You can even draw a wall around the existing IB that assumes IA by making an empty IAB that just inherits both and change existing types to use IAB. Probably a giant pain but then you'd be well set up to avoid perpetuating the existing mess and you can slowly break the older code up over time.

    YES!!!

    That might be easier than refactoring the whole thing.



  • @Captain said in Is it bad, how to fix, interface hierarchy.:

    Arguably, a form-like object is in that situation.

    In this case, I'm trying to change the way we do form fields. The existing base form functionality, that assumes the fields are the old way, is what I don't need, but I want to reuse the existing error functionality.


  • And then the murders began.

    @xaade said in Is it bad, how to fix, interface hierarchy.:

    The only pet peeve I have with CoI, is that if I have a composite class, that derives from the various interfaces to expose that functionality, I have to create the slew of methods just to forward the calls to the contained classes.

    I wish there was a way to automate that, for methods that don't need to communicate between the contained classes.

    Visual Studio 2019 (and I thought 2017 as well) will do that for you. If you declare a private member matching your interface, then add the interface to your class definition, the "quick fix" options for the red squiggly on the class name should include "implement interface via [private member]".


  • Notification Spam Recipient

    @xaade said in Is it bad, how to fix, interface hierarchy.:

    The problem is that they've created far too many dependencies between IB and IA.
    If you have an IB, they've assumed it's an IA.

    Oof, sorry about that.

    I'm in a similar situation with how our Hand Controller is written. It's become a god class in its own right because despite inheriting several interfaces (a movement interface, controller input interface, and a few others) it's cross-dependant with itself in many ways and I can't separate concerns without redoing a lot of the code (some 80% of the 6k lines).



  • @xaade said in Is it bad, how to fix, interface hierarchy.:

    @mikehurley said in Is it bad, how to fix, interface hierarchy.:

    Yeah, split them up. You can even draw a wall around the existing IB that assumes IA by making an empty IAB that just inherits both and change existing types to use IAB. Probably a giant pain but then you'd be well set up to avoid perpetuating the existing mess and you can slowly break the older code up over time.

    YES!!!

    That might be easier than refactoring the whole thing.

    Remember to do the refactoring in the future though, to get rid of having two ways to implement something, because it will rot.


  • Considered Harmful

    How I would do it:

    interface IA {}
    interface IB {}
    interface IBothBAndA: IA, IB {}
    

    And your IBothBAndA would be refactor-renamed from the old IB, so all the classes that used to implement IB now implement both and all the code that depends on them still works.


  • Banned

    @xaade said in Is it bad, how to fix, interface hierarchy.:

    If you have an IB, they've assumed it's an IA.

    I don't blame them - that's how the types were defined. IB implements IA literally means that every instance of IB is a valid instance of IA. Sometimes it makes sense, but as with all absolutes, it should be avoided unless you're really, really sure this is certainly the case and will continue to be for the next 100 years. And even then, often you shouldn't do that if you can afford not doing that.



  • @Gąska

    Yes, but these days we have

    IEnumerable<>.OfType<> and .Cast<>

    So, that pattern doesn't help anymore.


  • Banned

    @xaade well, if the devs know better than the type system, nothing will help you here.


  • Trolleybus Mechanic

    @Carnage said in Is it bad, how to fix, interface hierarchy.:

    @xaade said in Is it bad, how to fix, interface hierarchy.:

    @mikehurley said in Is it bad, how to fix, interface hierarchy.:

    Yeah, split them up. You can even draw a wall around the existing IB that assumes IA by making an empty IAB that just inherits both and change existing types to use IAB. Probably a giant pain but then you'd be well set up to avoid perpetuating the existing mess and you can slowly break the older code up over time.

    YES!!!

    That might be easier than refactoring the whole thing.

    Remember to do the refactoring in the future though, to get rid of having two ways to implement something, because it will rot.

    And bludgeon those who try to use IAB in new code.



  • @Gąska said in Is it bad, how to fix, interface hierarchy.:

    @xaade well, if the devs know better than the type system, nothing will help you here.

    ?

    I don't think you understood me.

    Sorry.


  • Banned

    @xaade sorry for not understanding you 🤷♂



  • @xaade - Just look at the "I" in SOLID....


Log in to reply