# Wie tief darf eine Vererbungshierarchie sein?



## Marco13 (19. Dez 2008)

Hi

Mal eine allgemeine ... fast schon philosophische Frage: Wie tief darf (oder sollte) eine Vererbungshierarchie sein? 

Tiefe Vererbungshierarchien sind vielleicht "theoretisch gut", in der Praxis können sie aber schwer wartbar oder schlicht unübersichtlich sein. Auf den Seiten, die man bei Websuchen diesbezüglich so findet (z.B. auf http://www.javaspecialists.eu/archive/Issue121.html ), steht im allgemeinen, dass eine Vererbungstiefe von ca. 5 noch OK ist, es aber in Richtung 10 schon kritisch wird. Grundsätzlich sehe ich die Problematik bei tiefen Hierarchien. Aber ich habe kaum Seiten gefunden, wo auf dieses Thema speziell in bezug auf Java *Interfaces* eingegangen wurde. Ich denke, dass da zwischen Interface-Hierarchien und Klassen-Hierarchien ein gewaltiger Unterschie ist, und diese "Grenzen" oder Aussagen bezüglich der maximalen Tiefe bei _Interfaces_ praktisch nicht greifen.

Der Anlass für meine Frage: Im Moment bastle ich an einer Art Bibliothek, wo im Moment schon eine "Vererbungs"tiefe von ca. 11 bis 12 vorkommt. Allerdings sind die ersten 7-8 Stufen dieser Vererbung ausschließlich interfaces. Der Teil der Hierarchie, der tatsächlich aus Klassen besteht, ist im Moment die obligatorische Abstract->Default->Special-Kette
interface X
-> class AbstractX implements X
--> class DefaultX extends AbstractX
---> class SpecialX extends DefaultX

Die Bibliothek soll ziemlich allgemein werden. D.h. es soll praktisch die Möglichkeit geben, in jeder Stufe der Interface-Hierarchie eine konkrete Klasse einzuklinken. Die Frage ist, ob man sich diese Möglickeit teilweise verbauen sollte, indem man krampfhaft versucht, die Interface-Hierarchie plattzudrücken, oder ob ich Recht habe mit der Annahme, dass sich die "Daumenregeln" zur maximalen Tiefe der Vererbungshierarchie effektiv nur auf echte *Klassen* beziehen.

Bin schon gespannt auf eure Meinungen.


----------



## maki (19. Dez 2008)

Meine Meinung:
Das Problem mit tiefen Vererbungshierarchien betrifft hauptsächlich Klassenvererbung, da u.U. das Verhalten über mehrere Generationen hinweg verfolgt werden muss.

Ansonsten würde ich vielleicht (?) soviel wie möglich vom Defaultverhalten in die Abstrakte Klasse packen und die Spezialimplementierung parallel zur Defaultimplmentierung stellen, ungefähr so:

interface X 
-> class AbstractX implements X 
--> class DefaultX extends AbstractX 
--> class SpecialX extends AbstractX

Nachtrag:
Ach ja, würde auch nicht allzuviel Vererben auch mit Interfaces, nur die wirklich notwendigen "zwischenschritte" der Interfaces angeben, verringert zwar die Flexibilität, aber erhöht die Übersicht.
Viele API bzw. Klassen/Interfacehierarchien werden falsch verwendet weil sie zu komplex sind, dann lieber "plattdrücken".


----------



## Ebenius (19. Dez 2008)

7-8 Schritte für Interfaces finde ich im allgemeinen etwas viel. Sicher gibt's dafür Anwendungsfälle, aber meisten vererbe ich Interfaces höchstens mit Tiefe 3. Ausnahme gibt's hier manchmal in GUI-Modellen, da geht's auch mal ein bisschen weiter.

// Nachtrag: Du solltest hauptsächlichen aufpassen, dass die Anzahl der implementierten Schnittstellen einer Klasse nicht überhand nimmt. Irgendwie muss man als Nutzer einer Klasse möglichst innerhalb kurzer Zeit überblicken können was sie kann und was nicht. Wenn eine Klasse dann 20 Interfaces implementiert, ist man schnell mal überfordert.


----------



## FArt (19. Dez 2008)

Wenn man eine Klasse viele Interfaces erfüllen lässt oder eine sehr große Vererbungshierarchie "benötigt", kann man meiner Meinung nach von einem Designfehler ausgehen.


----------



## Ebenius (19. Dez 2008)

FArt hat gesagt.:
			
		

> Wenn man eine Klasse viele Interfaces erfüllen lässt oder eine sehr große Vererbungshierarchie "benötigt", kann man meiner Meinung nach von einem Designfehler ausgehen.


Es sei denn man arbeitet mit Swing. :-D


----------



## tfa (19. Dez 2008)

Ebenius hat gesagt.:
			
		

> FArt hat gesagt.:
> 
> 
> 
> ...


Genau! Marco hat bestimmt mal wieder alles von JComponent abgeleitet


----------



## Marco13 (19. Dez 2008)

FArt hat gesagt.:
			
		

> Wenn man eine Klasse viele Interfaces erfüllen lässt oder eine sehr große Vererbungshierarchie "benötigt", kann man meiner Meinung nach von einem Designfehler ausgehen.



Darum ging es ja. Nur bin ich nicht sicher, ob ein Designfehler nur bei einer großen Hierarchie von Klassen vorliegt, oder auch schon bei einer großen Hierarchie von Interfaces. Das ist ja schon was anderes...

Ich habe zwar die Erfahrung gemacht, dass "einfache Beispiele" dazu führen, dass man "einfache Lösungen" für das Beispiel aufgezeigt bekommt, die in der konkreten Anwendung dann nicht viel weiterhelfen, aber ich hoffe mal auf die wohlwollende Einsicht der Antwortenden, dass das ein STARK vereinfachtes, plakatives Beispiel ist (Aber vielleicht sind die "einfachen Lösungen" ja trotzdem inspirierend  ) :


```
interface Lebewesen { void atme(); }
interface Tier extends Lebewesen { void esse(); }
interface Mensch extends Tier { void denke(); }
interface Programmierer extends Mesch { void programmiere(); }
```

Diese Interfaces sollen einen "Bibliotheks-Charakter" haben. D.h. es soll später Module geben, die sehr generisch sind, und mit beliebigen "Lebewesen" umgehen können. Und sie sollen diese auch NUR als solche kennen. Deswegen das Interface. Andere Module sollen mit "Tieren" umgehen können, und sie sollen sie AUCH NUR als solche kennen. Und so weiter. (Dazu kommen erschwerend ggf. evtl. noch einige "Mixin-Interfaces", die prinzipiell in jeder Stufe dazugenommen werden könnten, aber das ist fast schon ein anderes Thema).

Jede Stufe der Interface-Hierarchie hat also - vereinfacht gesagt - ihre Exsitenzberechtigung darin, dass ein Modul NUR die Methoden aus dieser Stufe kennen soll, und dass es potentiell konkrete Implementierungen gibt, die auch NUR die jeweilige Stufe implementieren. 



			
				FArt hat gesagt.:
			
		

> Wenn man eine Klasse viele Interfaces erfüllen lässt...



Ich bin mir nicht sicher, wie du in diesem Fall "die _Anzahl_ der Interfaces" definierst, die eine konkrete Klasse dann implementiert.  Ein "DefaultProgrammierer" implementiert ja nur eigentlich nur EIN Interface, nämlich "Programmierer" (damit zwar auch alle anderen, aber das ist ja gewünscht - an der Stelle, wo man das Programmierer-Interface kennt, braucht es einen ja in vielerlei Hinsicht nicht unbedingt zu interessieren, dass das vielleicht die 10. Stufe einer Hierarchie von Interfaces ist)


----------



## Ice-Tea (20. Dez 2008)

```
interface Lebewesen { void atme(); }
interface Tier{ void esse(); }
interface Mensch{ void denke(); }
interface Programmierer{ void programmiere(); }
```



wie wäre es denn damit:

eine klasse kann alles und implementiert viele kleine interfaces.

Dann nur noch ein cast und schon kann ein Mensch nur noch atmen aber nichts mehr essen, denn es ist ja kein mensch mehr, sondern "nur noch" lebewesen. zumindest stehen nicht mehr methoden zur verfügung.

Ist gar nicht so lange her, da hab ich mir auch gedanken über eine Hierachische Inferfacestruktur gemacht. Ich fand jedoch diese art ist leichter und besser zu implementieren.

UND - das ganze hat einen Vorteil: Du kannst einem Schwein das programmieren beibringen  :lol:


----------



## 0x7F800000 (21. Dez 2008)

Ice-Tea hat gesagt.:
			
		

> UND - das ganze hat einen Vorteil: Du kannst einem Schwein das programmieren beibringen


Ja, geil... Wenn du dir dann ein einfaches Schwein zur Herstellung von HotDogs erschaffst, fragt es dich dann erstmal nach einer fetten Villa, nem riesenauto und 20 Jahren zeit für ein Studium an der MIT.

dadurch gehen doch grad alle Vorteile verloren ???:L


----------



## Ice-Tea (21. Dez 2008)

Mein fehler...
Hast recht, die interfaces müssen schon erweitert werden.
Ohne Luft zum atmen lebt mein Schwein sonst nicht einmal lange genug um die große eines HotDogs zu erreichen.


----------



## Marco13 (21. Dez 2008)

Die Möglichkeit, eine Klasse alles implementieren zu lassen, fällt weg - aus dem Grund, den Andrey schon (etwas verschlüsselt :wink: ) genannt hat: Die Methoden der unteren Interfaces zu implementieren kann sehr aufwändig sein - und die Voraussetzungen dafür sind u.U. sehr komplex. Natürlich kann die Hierarchie der Klassen in gewisser Hinsicht "parallel" zu der der Interfaces verlaufen:

```
class AbstractLebewesen implements Lebeswesen { void atme() { System.out.println("Schnauf..."); }}
class AbstractTier extends AbstractLebewesen implements Tier
{ 
    // void atme() { System.out.println("Schnauf..."); }} // Muss nicht mehr implementiert werden: Schon in der abstrakten Klasse vorhanden....
    void esse() { System.out.println("Mampf..."); }
}
```
Aber das ändert ja nichts an der "zwingend"(?) vorgegebenen Tiefe der Interface-Hierarchie.

In gewisser Hinsicht gäbe es eine Lösung - die auch schon in der Collections-API von Sun verwendet wurde: Es gibt EIN Interface, wo ALLE Methoden drinstehen. Bei Methoden, die nicht unterstützt werden, wirft man eine "UnsupportedOperationException".  Das halte ich aber nicht für sinnvoll - habe gerade mal einen Thread zu diesem Thema eröffnet: Zum Sinn von "UnsupportedOperationException" - weil das hiermit eigentlich nurnoch sehr indirekt zu tun hat.

Sowas würde mir jedenfalls nicht so gut gefallen:

```
class Schwein implements Mensch
{
    ...
    void denke() { throw new UnsupportedOperationException("Oink?"); }
}
```
 :? 


(BTW: Als Alternativen hatte ich schon an das IAdaptable (oder "Extension Object") Pattern gedacht, aber das passt nicht so gut (keine Comiliezeit-Sicherheit, und zu kompliziert zu verwenden, wenn die Interfaces "oft" abgefragt werden müssen (und nicht nur EINmal, wie z.B. bei einem Plugin))


----------

