Was ist das Problem der fragilen Basisklasse in Java?
Antworten
Zu viele Anzeigen?Eine fragile Basisklasse ist ein häufiges Problem bei der Vererbung, das für Java und jede andere Sprache gilt, die Vererbung unterstützt.
Kurz gesagt, die Basisklasse ist die Klasse, von der Sie erben, und sie wird oft als fragil bezeichnet, weil Änderungen an dieser Klasse unerwartete Auswirkungen auf die Klassen haben können, die von ihr erben.
Es gibt einige wenige Methoden, um dies abzumildern, aber keine einfache Methode, um es vollständig zu vermeiden und trotzdem Vererbung zu verwenden. Sie können verhindern, dass andere Klassen von einer Klasse erben, indem Sie die Klassendeklaration als final
in Java.
Um die schlimmsten dieser Probleme zu vermeiden, empfiehlt es sich, alle Klassen als endgültig zu kennzeichnen, es sei denn, Sie beabsichtigen ausdrücklich, von ihnen zu erben. Wenn Sie beabsichtigen, von einer Klasse zu erben, entwerfen Sie sie so, als ob Sie eine API entwerfen würden: Verstecken Sie alle Implementierungsdetails; seien Sie streng bei dem, was Sie ausgeben, und vorsichtig bei dem, was Sie akzeptieren, und dokumentieren Sie das erwartete Verhalten der Klasse im Detail.
Alles, was Colin Pickard gesagt hat, ist wahr, aber hier möchte ich einige der besten Praktiken hinzufügen, wenn Sie Code schreiben, der diese Art von Problem verursachen kann, und besonders, wenn Sie ein Framework oder eine Bibliothek erstellen.
-
Machen Sie alle Ihre konkreten Klassen standardmäßig final, weil Sie wahrscheinlich nicht wollen, dass sie vererbt werden. Dieses Verhalten finden Sie in vielen Sprachen, z. B. in Kotlin. Außerdem können Sie, wenn Sie sie erweitern müssen, jederzeit die
final
Stichwort. Auf diese Weise wird das Fehlen vonfinal
für eine Klasse kann als Warnung interpretiert werden, sich nicht auf eine bestimmte Funktionalität für andere Methoden aufthis
die nichtprivate
y/ofinal
. -
Bei Klassen, die nicht als endgültig gekennzeichnet werden können, sollten Sie so viele Methoden wie möglich erstellen
final
um sicherzustellen, dass sie nicht von Unterklassen geändert werden. Außerdem sollten Sie keine Methoden freigeben, die nicht überschrieben werden sollen - bevorzugen Sieprivate
überprotected
. Gehen Sie davon aus, dass jede Methode, die nichtprivate
y/ofinal
überschrieben werden und sicherstellen, dass der Code Ihrer Oberklasse weiterhin funktioniert. -
Versuchen Sie, keine Vererbungsbeziehung ("Bar ist ein Foo") zu verwenden. Verwenden Sie stattdessen eine Hilfsbeziehung ("Bar verwendet ein Foo") zwischen Ihren Klassen. Verwenden Sie Schnittstellen anstelle von abstrakten Klassen, um sicherzustellen, dass die Klassen, die diese Hilfsklasse verwenden, eine einheitliche Schnittstelle haben.
-
Denken Sie daran, dass fast* jeder
extends
kann ersetzt werden durchimplements
. Dies ist der Fall, wenn Sie eine Standardimplementierung wünschen; ein Beispiel für eine solche Konvertierung ist unten dargestellt:
Alter Code:
class Superclass {
void foo() {
// implementation
}
void bar() {
// implementation
}
}
class Subclass extends Superclass {
// don't override `foo`
// override `bar`
@Override
void bar() {
// new implementation
}
}
Neuer Code:
// Replace the superclass with an interface.
public interface IClass {
void foo();
void bar();
}
// Put any implementation in another, final class.
final class Superclass implements IClass {
public void foo() {
// implementation for superclass
}
public void bar() {
// implementation for superclass
}
}
// Instead of `extend`ing the superclass and overriding methods,
// use an instance of the implementation class as a helper.
// Naturally, the subclass can also forgo the helper and
// implement all the methods for itself.
class Subclass implements IClass {
private Superclass helper = new Superclass();
// Don't override `foo`.
public void foo() {
this.helper.foo();
}
// Override `bar`.
public void bar() {
// Don't call helper; equivalent of an override.
// Or, do call helper, but the helper's methods are
// guaranteed to be its own rather than possibly
// being overridden by yours.
}
}
Dies hat den Vorteil, dass die Methoden der Oberklasse sicher sein können, dass sie miteinander arbeiten, aber gleichzeitig können Sie Methoden in Ihrer Unterklasse überschreiben.
*Wenn Sie tatsächlich wollten, dass die Oberklasse Ihre überschriebene Methode verwendet, haben Sie mit diesem Ansatz Pech gehabt, es sei denn, Sie wollen auch alle diese Methoden in der "Unterklasse" neu implementieren. Abgesehen davon kann es verwirrend sein, wenn die Oberklasse die Unterklasse aufruft, so dass es gut sein kann, diese Art der Verwendung neu zu bewerten, ungeachtet der Inkompatibilität mit diesem Ansatz.