Verweigerte Vererbung
🚧 | Diese Seite befindet sich in Bearbeitung | 🚧 |
🤓 | Diese Seite ist eine Bewertungsrichtlinie, die ab Blatt 3 annotiert und ab Blatt 4 abgezogen wird. | 🤓 |
Beschreibung
Wie wir in Polymorphie gelernt haben, können wir Klassen als abstract deklarieren um verwandte Eigenschaften an Kind-Klassen zu vererben. Es kann sein, dass wir allerdings dort doch nicht alle Eingenschaften implementieren wollen oder sogar können.
In dem Fall liegt es nahe, innerhalb der Methode null zurückzugeben oder den Block leer zu lassen.
Ein leerer Block ist allerdings gegen die Konvention. Bleibt also nur noch, in dem Fall, dass die Methode einen Rückgabetyp außer void hat, die Methode mit einem null-Return zu füllen um so den Compiler glücklich zu machen. Allerdings sind wir damit nicht glücklich.
Warum? Wir brauchen die Methode doch eh nicht.
Wir bezeichnen dieses Vorgehen als Anti-Pattern. Genauer wird dieses Anti-Pattern abgelehnte Vererbung genannt ("Refused Bequest").
Hierbei beziehen wir uns auch die oben bereits beschriebene Situation:
Wir stellen eine Eltern-Klasse bereit, von der die Kind-Klassen erben. Diese akzeptieren oder verwenden jedoch nur einen Teil der bereitgestellten Methoden (z.B. eben genau dadurch, dass wir null zurückgeben oder die Methode leer lassen). Das führt zu einer unnötigen Komplexität und einer instabilen Codebasis führen. In einem guten Implementierungsbeispiel würde die abgeleitete Klasse die vererbte Funktionalität sinnvoll nutzen und erweitern. Die Beziehung zwischen der Eltern- und Kindklasse würde die Prinzipien der Vererbung und der Einzelverantwortlichkeit beachten.
Wollen wir Eigenschaften wirklich nicht allen Kind-Klassen übermitteln, so wäre eine geeignetere Lösung, diese Eigenschaft als Interface zu modellieren, statt sie in einer Eltern-Klasse vorauszusetzen. (Erinnerung: Wir können nur einmal erben (extends) aber beliebig oft implementieren (implements)).
Negativbeispiel
public class Bird {
public void fly() {
// Implementation for flying
}
}
public class Penguin extends Bird {
@Override
public void fly() {
// Do nothing, the penguin does not fly :(
}
}
Positivbeispiel
public interface Flying {
void fly();
}
public class Bird {
// Bird class does not implement the fly method directly
}
public class Penguin extends Bird {
// Penguin class does not need to implement the fly method
}
public class FlyingBird extends Bird implements Flying {
// Every concrete bird that can fly can inherit from this class
@Override
public void fly() {
// Implementation for flying,
}
}