While examining some other code I’ve encountered a bunch of stuff which makes me wonder what they’re teaching people nowadays in school. Rather than silently complain about the problem to my co-workers, of course I decided instead to use this forum to rant.
Today’s anti-pattern: improper inheritance.
The problem: A class ‘ClassA’ is defined which contains two features–call them ‘feature 1’ and ‘feature 2’. A class ‘ClassB’ needs to adopt ‘feature 1’ in ‘ClassA’, but also add new features which are somewhat orthogonal to the functionality in ‘ClassA’, ignoring ‘feature 2.’
When a software maintainer comes around to change ‘feature 2’ in ClassA, he finds that ClassB breaks–ClassB was written in such a way as to hide or never initialize ‘feature 2’ within ClassA in the first place.
Why this happened: The software developer writing ClassB saw some of the code he wanted to reuse, but rather than deal with the problem of properly abstracting ‘feature 1’ out of ClassA, he instead took advantage of subclass-level visibility of the internal fields in order to bypass ‘feature 2’.
What the developer should have done: Take ‘feature 1’ out of ClassA and create an abstract super-class ‘ClassC’, then rewrote ‘ClassA’ and created ‘ClassB’ to inherit from ‘ClassC.’ Thus it’s clear that the functionality in ‘feature 2’ isn’t used in ClassB, and ClassB doesn’t have to rely upon supernatural knowledge of ‘ClassA’ which could be broken in an upgrade or bug fix.
What this illustrates: A class in object oriented design should be a self-contained unit of functionality. If a class is well designed, it should have well-designed extension points and a well-designed feature set which acts as a single unit.
When well designed, other classes do not have to rely upon super-natural knowledge of the implementation of a class in order to operate correctly; the implementation knowledge–while relatively transparent so that users can know how the black box should operate–should be implemented as if it were a black box.
When a subclass has to rely upon supernatural knowledge or relies upon knowing which calls touch certain internal states related to a feature that the subclass doesn’t want to enable or use, then this clearly shows the need to refactor the superclass.