We learned at school that inheritance represents an 'is-a' relationship. It is not.
TL;DR: Think about protocol and behavior, forget inheritance
-
Bad models
-
Unexpected behavior
-
Subclass overrides
-
Liskov substitution principle Violation
-
Think in terms of behavior behaves-as-a
-
Prefer composition over inheritance
-
Subclassify always following 'behaves-as-a' relation
IS-A relation comes from the data world.
We learned ERDs with structured design and data modeling.
Now, we need to think in terms of behavior.
Behavior is essential, data is accidental.
class ComplexNumber {
protected double realPart;
protected double imaginaryPart;
public ComplexNumber(double realPart, double imaginaryPart) {
this.realPart = realPart;
this.imaginaryPart = imaginaryPart;
}
}
class RealNumber extends ComplexNumber {
public RealNumber(double realPart) {
super(realPart, 0);
}
public void setImaginaryPart(double imaginaryPart) {
System.out.println("Cannot set imaginary part for a real number.");
}
}
class Number {
protected double value;
public Number(double value) {
this.value = value;
}
}
class ComplexNumber extends Number {
protected double imaginaryPart;
public ComplexNumber(double realPart, double imaginaryPart) {
super(realPart);
this.imaginaryPart = imaginaryPart;
}
}
class RealNumber extends Number { }
[X] Manual
This is a semantic smell.
- Inheritance
Real Number IS-A Complex number (according to math).
Integer IS-A Real number (according to math).
Real Number does not Behave-Like-A Complex number.
We cannot do real.setImaginaryPart() so it is not a Complex according to our Bijection
Code Smell 92 - Isolated Subclasses Names
Code Smell 11 - Subclassification for Code Reuse
Code Smell 37 - Protected Attributes
Photo by Joshua Rondeau on Unsplash
DRY - Don't Repeat Yourself - Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.
Andy Hunt
Software Engineering Great Quotes
This article is part of the CodeSmell Series.