# Checkstyle: Wann ist eine Methode für Vererbung entworfen?



## Lowpass (28. Apr 2010)

Zur Zeit lasse ich meinen Code durch Checkstyle prüfen und erhalte bei (fast?) allen Methoden die Meldung, dass diese nicht für Vererbung entworfen worden ist und darum als final deklariert werden soll.

Nun frage ich mich: ab wann _ist_ denn eine Methode für Vererbung entworfen? bzw. anders gefragt: wie deklariert man eine Methode korrekt für Vererbung?

...oder interessiert sich Checkstyle am Ende nur dafür, ob es bereits konkrete Klassen gibt, die von der aktuellen Klasse ableiten und die in Frage kommende Methode bereits überschreiben?


----------



## Marco13 (28. Apr 2010)

Ich kenne die konkrete Meldung von CheckStyle nicht, aber rein theoretisch-formal und bei (übertrieben?) konsequenter Auslegung des Open/closed principle - Wikipedia, the free encyclopedia könnte man sagen: Alles, was final sein kann, sollte final sein. D.h. wenn man eine Methode definiert, die nicht überschrieben werden soll, dann "sollte" man die schreiben als

```
final void method(final int a, final Object b, final String c) { ... }
```

Konkret steht dazu ja auch hier was: Checkstyle - Class Design...


----------



## Lowpass (28. Apr 2010)

Es ist mir klar was zu tun ist, wenn eine Methode _nicht_ für Vererbung entworfen ist - aber die Frage ist ja genau das Gegenteil: wann _ist_ eine Methode für Vererbung entworfen?

Allerdings hilft da Dein Link (danke dafür):
The exact rule is that nonprivate, nonstatic methods of classes that can be subclassed must either be
- abstract or
- final or
- have an empty implementation

Finde ich allerdings ganz schön krass - und mir zudem nicht verständlich:

Denn dann wäre Object.toString ebenfalls nicht für Vererbung entworfen und Checkstyle würde warnen? toString ist weder abstract, noch final, noch leer - und ist _absolut_ für Vererbung entworfen.


----------



## maki (28. Apr 2010)

Finde ich auch "krass", aber vor allem unpräzise.
Manchmal will man eben eine Default-Implementierung bereitstellen... denke dass diese Regeln nicht wirklich zu eng zu sehen sind, sondern der Tatsache, dass die meisten OO Sprachen kein Sprachkonstrukt haben welches aussagt, das etwas überschrieben werden, kann, aber nicht muss.


----------



## fastjack (28. Apr 2010)

Whl. muß die default-Implementierung dann in eine Default-Klasse, in der sie dann final ist. Ich denke eher, das ist Checkstyle-Logik.


----------



## maki (28. Apr 2010)

fastjack hat gesagt.:


> Whl. muß die default-Implementierung dann in eine Default-Klasse, in der sie dann final ist.


Meinst du final Methoden?
Die sind dann nicht nur default, sondern auch nicht überschreibbar 

Das Problem ist mE dass man nur sagen kann, dass etwas nicht überschrieben werden kann (final), oder aber, dass etwas überschrieben werden muss (abstract,  dann geht es aber nicht mehr um das Überschreiben sondern um das implementieren), aber nicht, das etwas überschrieben werden kann, aber nicht muss.


----------



## fastjack (28. Apr 2010)

Ich halte das wie Maki. Checkstyle, ebenso wie Buildsysteme, die meckern, wenn ein Text dynamisch geladen wird, lassen sich auch umkonfigurieren. Ich denke die default-Einstellung eines Tools ist nicht immer die beste Wahl.

[edit]

Ein nettes Beispiel auf deutsch:

Java: Erweiterungen von Klassendeklarationen für Exemplare

[/edit]


----------



## Lowpass (28. Apr 2010)

Klar kann man Checkstyle umkonfigurieren und ich schrecke auch nicht davor zurück, das zu tun - nur würde ich eben vorher gern wissen, was sich die Väter der Default-Konfiguration dabei überlegt haben.

Checkstyle meckert per default auch, wenn man abstrakte Klassen definiert. Es heisst dann, man soll besser ein Interface daraus machen - da bin ich aber gänzlich anderer Meinung. Ein Interface kann von jeder beliebigen Klasse implementiert werden, aber um von einer abstrakten Klasse abzuleiten, muss man sich da in die Klassenhierachie einfügen - das war in meinem Fall eine bewusste Einschränkung und ausserdem konnte ich bestimmte Methoden bereits in der abstrakten Klasse implementieren. Zudem halte ich mich gerne an einen einfachen Grundsatz, den ich vor langer Zeit mal gelesen hatte: die Superklasse gibt an, was eine Klasse _ist_ und die implementierten Interfaces geben an, was eine Klasse _kann_.

Ebenfalls nicht einverstanden bin ich mit dem Grundsatz, dass alle Member private sein müssen. Ich habe einen Fall, in dem ich in der abstrakten Oberklasse bestimmte Methoden bereits komplett implementiere - und da drin brauche ich einen logger. Der Logger wird aber erst in den Unterklassen initialisiert (klar: von der abstrakten Klasse kann es ja keine Instanzen geben und dem Logger wird bei der Erzeugung der Name der instanzierten Klasse, in welcher er verwendet wird, mitgegeben). Da ist es viel einfacher, den logger protected zu machen, anstatt dann in den Unterklassen jedesmal mit getLogger darauf zuzugreifen...

Aber eben... ich suche ja bewusst die Diskussion für solche Fragen, um die Ideen hinter den Einschränkungen besser zu verstehen und gegebenenfalls mich oder die Checkstyle-Konfiguration anpassen zu können. Natürlich ist das nicht der Weisheit letzter Schluss, das ist mir klar.


----------



## maki (28. Apr 2010)

Der Weg
Interface -> abstrakte Klasse -> kontrekte Klassen bietet mehr flexiblität als abstrakte Klasse -> konkrete klasse und gleichzeitig mehr Entkopplung 

Dein Beispiel mit dem logger finde ich nicht gut, sowas gehört nicht in die Vererbungshierarchie, mit aktuellen Loggern (LOG4J, etc.) will man sowieso einen Logger pro loggender Klasse.


----------



## Lowpass (28. Apr 2010)

hmmm... ich dachte es sei vielleicht nicht so logisch, wenn eine Klasse loggt, die gar nicht instanziert werden kann...

Was die Sache mit Interface -> abstrakte Klasse betrifft: und wenn ich diese Flexibilität bewusst nicht will? Wenn ich nicht will, dass sich da jeder nach Belieben reinhängen kann?


----------



## maki (28. Apr 2010)

Beid er genannten Flexibilität geht es darum, die Nutzer deiner Klassen (interfaces etc.) vor Änderungen zu schützen, kannst dir ja mal die Collections API ansehen.
Und abhängigkeiten zu reinen Interfaces sind immer loser als Abhängigkeiten zu Klassen, selbst abstrakten.


----------



## Lowpass (28. Apr 2010)

maki hat gesagt.:


> Und abhängigkeiten zu reinen Interfaces sind immer loser als Abhängigkeiten zu Klassen, selbst abstrakten.



Ich rede ja nicht allgemein, sondern von Fällen, in denen man eben _keine_ lose Abhängigkeit wünscht.
Abgesehen davon würde ich gerne mal wissen, wieso Abhängigkeiten zu Interfaces loser sind als zu Klassen:
Das Interface wirst Du kaum ändern können, ohne nicht auch alle implementierenden Klassen zu ändern. Bei abstrakten Klassen kannst Du nicht-abstrakte Methoden beliebig hinzufügen ohne am bestehenden Code irgend etwas ändern zu müssen.

Du meinst wahrscheinlich eher, dass die Abhängigkeiten zur implementierenden Klasse geringer sind.


----------



## maki (28. Apr 2010)

> Ich rede ja nicht allgemein, sondern von Fällen, in denen man eben keine lose Abhängigkeit wünscht.


Wann sollte das denn der Fall sein?



> Abgesehen davon würde ich gerne mal wissen, wieso Abhängigkeiten zu Interfaces loser sind als zu Klassen:


Das gehört zu den Grundlagen 
Nur von einer Schnittstelle abhängig zu sein bezeichnet man als lose Kopplung(vereinfacht ausgedrückt), anstatt von konkreten klassen abhängig zu sein.
Abhängigkeit zu einer Schnittstelle vs. Abhängigkeit zu einer Implementierung!
Denn wenn ich nur vom Interface abhängig bin, brauche ich mir über die Implementierung keine Gedanken zu machen.

Nebenbei, es geht nicht nur darum Änderungen einfacher zu machen, sondern wirklich darum, die Implementierungen auf Wunsch auszutauschen, sehr häufig beim Unit testen zu beobachten (Mocks, Stubs, Fakes...)


> Das Interface wirst Du kaum ändern können, ohne nicht auch alle implementierenden Klassen zu ändern. Bei abstrakten Klassen kannst Du nicht-abstrakte Methoden beliebig hinzufügen ohne am bestehenden Code irgend etwas ändern zu müssen.


Das ist doch genau der "Trick" wenn ich Interface <- abstrakte Klasse <- konkrete Klasse habe, ich kann die neuen Methoden in der Abstrakten Klasse implementieren (default).
Das ich das Interface ändern muss wenn ich Methoden hinzufüge ist klar.


----------



## Lowpass (28. Apr 2010)

maki hat gesagt.:


> Wann sollte das denn der Fall sein?


Wenn man nach aussen hin nicht erweiterbar sein möchte.



> Das gehört zu den Grundlagen
> Nur von einer Schnittstelle abhängig zu sein bezeichnet man als lose Kopplung(vereinfacht ausgedrückt), anstatt von konkreten klassen abhängig zu sein.


Aber auch die Grundlagen muss man erst einmal verstehen.
Diese lose Kopplung bezieht sich auf die Unabhängigkeit gegenüber den konkreten Implementierungen, nicht aber auf die verwendeten Interfaces.
Änderst Du die Interfaces, musst Du nicht nur den nutzenden Code anpassen, sondern auch sämtliche Implementierungen.
Zudem: alles zu 100% flexibel und austauschbar zu machen entspricht dem YAGNI Anti-Pattern.



> Nebenbei, es geht nicht nur darum Änderungen einfacher zu machen, sondern wirklich darum, die Implementierungen auf Wunsch auszutauschen


In meinem Fall gibt es 2 Implementierungen der abstrakten Klasse - und diese können sich nicht gegenseitig ersetzen, sondern existieren nebeneinander - sind 2 Ausprägungen der selben Ober-Klasse. Und es _gibt_ nur diese zwei und _darf_ nur diese zwei geben.
Und falls es eine dritte geben soll, dann will ich sicher sein, dass diese nur und ausschliesslich zu diesem Zweck existiert - und das kann ich ein kleines bisschen erzwingen, indem es eine Subklasse sein muss und nicht einfach ein Interface als eines von womöglich vielen implementieren kann.
Ich will eine 100%ige Unterordnung in die Klassenhierarchie erzwingen.


----------

