Mehrfachvererbung umgehen

Hey Leute.

Ich hab hier eine echte anfänger frage…
undzwar, wie würdet ihr folgenden fall umsetzen?

Ihr habt 3 klassen:

....
}```

und 

class SuperSpecialPathNode {

}


und

```SuperSpecialPathNodeRenderObject {
....
}```

am liebsten würde ich die dritte klasse von den ersten beiden erben lassen. geht in java ja nicht.
Wie löst man das am besten? Was mir dazu nur einfällt ist:

```interface RenderObjectImplementation {
    RenderObject getRenderObjectImplementation();
}

class SuperSpecialRenderablePathNode extends SuperSpecialPathNode implements RenderObjectImplementation {
     private RenderObject renderObjectImplementation;
     ....
}```

oder halt:

```interface SuperSpecialPathNodeImplementation {
   SuperSpecialPathNode getSuperSpecialPathNodeImplementation();
}

class SuperSpecialRenderablePathNode extends RenderObject implements SuperSpecialPathNodeImplementation {
   private SuperSpecialPathNode superSpecialPathNodeImplemetation;
   ....
}```

ist das so richtig? also "macht man das so" ?
Und in diesem fall, wenn das richtig ist, welche der beiden versionen wäre "richtiger", kann man das auf
anhieb sagen, oder hängt es vom kontext ab?

Der Name SuperSpecialPathNodeRenderObject spricht jetzt zunächst mal dafür, dass die Klasse zuviel kann. Ich vermute das könnte man in zwei Klassen aufspalten, so dass diese Problematik gar nicht aufkommt.

Nein nein dass soll eine klasse sein. außerdem ist es doch schon in 2 klassen unterteilt. jetzt sollen die vereint werden.

verkürzt: RenderObject und Node

Hmm… was ist ein RenderObjekt und was ist eine Node? Was ist deine Intention bei dieser deiner Klassenplanung? Wenn Nodes sozusagen “Unterobjekte” eines RenderObjects sein sollen, dann vergiss den ganzen Kram mit n verschiedenen Klassen…
Mach aus dem RenderObject schlicht eine Klassenstruktur, die etwa einer List ähnelt.

}```
So ergibt sich immer und überall eine Baumstruktur von RenderObjects (RenderTree), die ganz simpel durch jeden Renderer abgearbeitet werden können.

a) Warum nicht Komposition? ist die einzige Möglichkeit in Java, dazu ein “zusammenfassendes Interface” und Delegation an die Member-Objekte

b) ab Java 8 können Interfaces jetzt auch default-Implementierungen haben: wenn du eine deiner Klassen so umschreiben kannst, dass die implementierten Methoden keine großartigen Gimmicks aufführen, dann kannst du das verwenden

Genauer gesagt kann jede zustandslose(!) Klasse zu einem Interface mit Default-Methoden werden, und dieses Interface dann wie gewohnt implementiert werden.

naja, renderobject ist halt ein opengl render objekt, und Node ist eben ein Node aus dem AStern-Projekt, das ich jetzt quasi
verallgemeinert haben wollte…

[ot]Side-Note : unglückliche Wahl des Interface-Namens : “Implementation” gehört wenn dann in den Namen der implementierenden Klasse, nicht in den Namen des Interface, und wird meist nur mit “Impl” abgekürzt, ähnlich wie du es bei deiner “Abstract…” gemacht hast (auch wenn mir da jetzt in der Signatur natürlich das Keyword, aber is ja nur pseudo-Code).[/ot]

wobei ‘Object’ selber auch schon bedenklich klingt,
aber wenn man sich als Alternative Node, Item oder sonstiges ausdenken müss (oder Renderable),
dann muss ja dieses wichtige Wort der Sprache nicht einfach ganz gestrichen sein, nur weil in Java alles Object ist,
in der API kommt es gelegentlich auch in Klassennamen vor


[quote=mymaksimus]```interface RenderObjectImplementation {
RenderObject getRenderObjectImplementation();
}

dieses Interface ist in sich ganz schön wiedersprüchlich,
was ist RenderObjectImplementation zu RenderObject? eine genauere Variante?

wieso kann die Klasse, die RenderObjectImplementation ist, vielleicht ein höheres RenderObject ist, ein RenderObject zurückgeben?
wieso ist der Rückgabewert dieser Methode nicht RenderObjectImplementation?

wann immer es in einem Programm kurze oder lange Klassennamen AbcDefGhi gibt (genauso API-Klassen, einfach immer), 
und dann irgendwo einen genau abgestimmten Methodennamen getAbcDefGhi(), dann muss diese Methode AbcDefGhi liefern!, alles andere ist doch verrückt

zurück zum Interface wäre das aber auch wieder eine Bredouille: 
was anderes sollte das das Interface implementierende Objekt zurückgeben als sich selber?
und wozu dann diese Methode überhaupt benötigt?

sicher steckt irgendein anderer Sinn dahinter der das besser erklärt, aber an der Klasse kaum sichbar,

zwei lose Beispiele:
```interface RenderObjectHolder {
    RenderObject getRenderObject(); // man beachte Methodenname und Rückgabetyp
}

interface RenderObjectFactory {
    RenderObject createRenderObject(); // man beachte Methodenname und Rückgabetyp
}

A* ist ein Algorithmus für kürzeste Pfade.

Das könnte man für verschiedene Typen verwenden. z.B. Städte. Dann wären die Nachbarn über die Strassen, die zu anderen Städten führen erreichbar und die Kostenfunktion würde sich an z.B. den Koordinaten orientieren.

Hört sich also ziemlich nach Generics an. mMn.

Also würde ich an der Stelle zu Node tendieren.

Collections, z.B. List sind auch Generics, mit einem Comparator kann man sortieren lassen. (A* bräuchte hier z.B. eine Kostenfunktion)

Bei den Collections würde man ja auch nicht auf die Idee kommen ein ListItem einzuführen um dann zu versuchen von RenderObject und ListItem zu erben um das ganze in einer ListOfListItems zu verwenden.

ein generischer Parameter an der Gesamtmenge ist bisschen was anderes als an den einzelnen Nodes,

und in einer Liste interessiert man sich ausschließlich dafür, welchen Typ die Elemente haben,
man braucht keine Methoden (außer die von Object wie equals/ hashcode)

das hilft hier nicht, zu jeden Eintrag muss real Information abgelegt werden

Komposition ist der richtige Weg,
selbst wenn Mehrfachvererbung ginge will man oft nicht seltsame A*-Methoden dauerhaft enthalten haben,
denkbar ist gar Verzicht auf die Render-Anteile, je nach Programmaufbau

in einer LinkedList ist es genauso aufgebaut: interne Klasse Entry für die next/ previous-Verknüpfung + dem eigentlichen Element

Also das RenderObject ist für die Darstellung zuständig und das Node Objekt hält die relevanten Daten. Wieso müssen die jetzt zu einem Objekt zusammengefasst werden? Stichwort MVC.

[QUOTE=bERt0r]Also das RenderObject ist für die Darstellung zuständig und das Node Objekt hält die relevanten Daten. Wieso müssen die jetzt zu einem Objekt zusammengefasst werden?[/QUOTE]Eben… würd ich gar nicht machen…
Ich geh mal davon aus, dass eine Node ein Stützpunkt des errechneten Pfades ist, also darstellbare Koordinaten enthält. Nun willst du ein RenderObject haben, welches diese Stützpunkte darstellt.
Von dem RenderObjekt nehm ich mal an, dass ihm darstellbare Punkte (z.B. Point3D) oder Polygone hinzugefügt werden können.
Wenn das so ist, dann lass Node halt von Point3D erben und gut ists.
(Aber ich spekuliere hier eh’ nur)

Mal so in den Raum geworfen, in java 8 sollte die Funktion:

 public <NODETYPE> ArrayList<NODETYPE> findPath(NODETYPE from, NODETYPE to, Function<NODETYPE, Set<NODETYPE>> getNeighbours, BiFunction<NODETYPE, NODETYPE, Float> calculateEstimatedDistance);

eigentlich reichen um jeden erdenklichen graph zu duchsuchen. Sprich es wäre eigentlich nicht mehr nötig dass ein “Node” irgentwelche intefaces implementiert.

Ein möglicher Aufruf wäre:

findPath(new Vector3f(0, 0, 0), new Vector3f(0, 100, 0), Vector3f::getNeighbours, Vector3f::distanceTo);

was sind getNeighbours und distanceTo, eigenimplementierte Methoden wo?
und die Rückgabe wäre Liste von Vector3f, welche in Liste von benötigten Nodes zurückzurechnen ist?

was ist dabei der Anteil von Java8, Methoden als Parameter zu übergeben?

natürlich kann man das Problem immer auf irgendeine Weise lösen:
die variablen Objekte bekommen als Minimum per Interface eine Methode, mit der sie ihre Gitter-Position herausrücken (leichter als extra getNeighbours-Methode usw.),
alles andere kann der Algorithmus alleine machen, mit eigenen Hilfsobjekten, die Nachbar- und Distanz-Berechnungen für den Anfang auch simpel aus den Koordinaten nach Gitter,
alles allein auf die Koordinaten gerechnet, am Ende zum Pfad die zugehörigen Objekte bestimmen,

das geht in Java wie in jeder höheren Programmiersprache seit der ersten Version