OOP Sehr allgemeine Schnittstelle

Luk10

Top Contributor
Grüße,

ich brauche für meine Situation eine sehr allgemeine Schnittstelle: Meine Grafikanwendung soll mehrere verschiedene Renderer-Implementierungen zulassen. Einige dieser Implemtierungen brauche aber Zusatz-Informationen, die sich sehr stark von Implementierung zu Implementierung unterscheiden.

Diese Informationen möchte ich in eine Klasse von Informations-Objekten packen, die eine gemeinsame Schnittstelle haben, deren konkrete Implementierung von Fall zu Fall unterschiedlich(st) ausfällt:

<Interface> Renderer
<Konkrete Implementierung> Renderer A braucht spezielle Information a
<Konkrete Implementierung> Renderer B braucht spezielle Information b

<Interface> Infomation Wie soll das hier aussehen?
<Konkrete Implementierung> Information a
<Konkrete Implementierung> Information b


Jedoch kann ich, wenn ich diese Schnittstelle als Interface umsetzte, keine Methoden finden, die dieses Interface anbieten kann, da die Art und der Typ von Information zu stark variiert.

Ist es "schlechtes" Design wenn das Interface keine Methoden hat und ich in der Implementierung dann auf die konkrete Implementierung casten muss um die speziellen Methoden abzufragen? Wie löst man sowas am besten?

Hoffentlich habe ich mich einigermaßen verständlich ausgedrückt.
 
Mit HashMaps ;D

Spaß ^^
---

Was du machst ist im Konstruktor die entsprechenden Informationen zu übergeben.

Und render() haben ja allle gemeinsam, da sonst sie keine Renderer wären.
 
Das geht glaube ich nicht.

Bei mir nimmt der Renderer "renderable"-Objekte an und zeichnet diese auf den Bildschirm. Alle renderable-Objekte haben eine Informations-Objekt.

Nun weiß der Renderer aber nicht was für ein Typ von Information das ist müsste einen cast auf den speziell gebrauchten Informations-Type durchführen. Meine Frage ist es, ob das gutes Design ist oder es eine bessere Lösung gibt.
 
Wenn du casten musst, dann ist das eher ein Hinweis auf schlechtes Design, da die Klasse mit Informationen umgeht, die ihr einfach nicht genügen.

Mach den Renderer Stateless und du kannst die benötigten Informationen in einem Kontext, der sie auch kennt immer übergeben.
 
Mach den Renderer Stateless und du kannst die benötigten Informationen in einem Kontext, der sie auch kennt immer übergeben.

Kannst du das etwas ausführen? Das Problem ist ja, dass es mehrere Renderer-Informationstypen Paare gibt.
 
Schwierig. Ich werde deinen Anwendungsfall bestimmt nicht treffen:

Ich habe einen Kontext K: Er sucht Daten auf der Datenbank und erhält Beans zurück.

Den Kontext K habe ich nun in zwei Ausprägungen, die sich lediglich durch die Art und Weise der Darstellung ändern. Die Darstellung wird durch den Renderer R gemacht.

Also Leite ich von K ab und habe K1 und K2.

K1 und K2 haben die konkreten Daten und können so den Render 1 und 2 entsprechend bestücken.

K1 und K2 hängen natürlich von der Anwendung ab. Hier steht dan sowas:

Wenn klick 1 dann K1 wenn Klick 2 dann K2.
 
Bisher klingt das, als könnte das zumindest im oberflächlichsten Sinn pragmatisch mit Generics gelöst werden:
Java:
interface Renderer<T>
{
    void render(RenderedObject<? extends T> object);
}

class RendererFoo implements Renderer<Foo>
{
    @Override 
    public void render(RenderedObject<? extends Foo> object)
    {
        Foo foo = object.getInformation();
    }
}

class RendererBar implements Renderer<Bar>
{
    @Override 
    public void render(RenderedObject<? extends Bar> object)
    {
        Bar bar = object.getInformation();
    }
}


class RenderedObject<T>
{
    private T information;
    public T getInformation() { return information }
}


// Verwenden:

Renderer<Foo> rendererFoo = new RendererFoo();
RenderedObject<Foo> foo = new RenderedObject(new Foo());
RenderedObject<Bar> bar = new RenderedObject(new Bar());

rendererFoo.render(foo); // Geht
rendererFoo.render(bar); // Geht nicht

Aber ich weiß, dass die Beschreibung bisher so allgemein war, dass da etwas dahinter stecken KÖNNTE, was man definitiv NICHT so einfach lösen kann...
 
Hmm ... an Generics hatte ich noch nicht gedacht.

Der Sinn des ganzen soll halt sein, dass "Renderables" von jeder Renderer-Implementierung angenommen werden können, jedoch die Zusatz-Information unterschiedlich sein kann.
Ich möchte die Zusatz-Information jedoch nicht direkt im "Renderable" haben, da dass dann wieder nicht für jeden Renderer passen würde. Deshalb eine Schnittstelle für Information.

Wenn jetzt aber diese Information von "Renderable" gefragt wird kann auch nur eine allgemeine Information geben werden.

Der Renderer braucht jedoch seine spezifische...
 
Also ich bin mir nicht sicher, aber ich glaube du kommst ohne Casts einfach nicht aus. Du hast zwei voneinander unabhaengige Ableitungshierarchien (Renderer und Renderable) und willst quasi eine Bruecke zwischen beiden Hierarchien bauen. Vielleicht hilft das schon: http://de.wikipedia.org/wiki/Brücke_(Entwurfsmuster)

Ansonsten wuerd ich das mit diesen Informationen ueberdenken. Was genau speicherst du da Renderer-spezifisches?

Zusaetzlich wuerd ich noch einwerfen, dass sich in meinen Projekten ein Interface Renderable als nicht so guenstig erwiesen hat - inzwischen mach ich lieber fuer jedes darzustellende Objekt eine eigne Render-Klasse, wo genau eine (public) render Methode drin ist:
Java:
public class Entity { ... }
public class EntityRenderer<E> {
  public render( E entity ) {
     ...
  }
}
 
Ansonsten wuerd ich das mit diesen Informationen ueberdenken. Was genau speicherst du da Renderer-spezifisches?

Dem würde ich mich anschliessen. Generel gesagt sollte ein Renderable Object von der Renderer implementierung entkoppelt werden. Ein Renderable sollte nur informationen über seinen Status halten, aber keine Infos wie es gerenderet werden muss
 
Da fällt mir nur die Problematik zu ein, die ich selbst grad auch habe. Es geht darum ordinäre 3D Objekte aus x-beliebigen 3D Formatdateien zu laden und zur Anzeige zu bringen. Trivial ist das wohl nicht.
Ich mach das grad' so, dass diese RenderableObjects eine Tag- bzw. Infoliste führen und dazu die Methoden add-, get- und removeTag(s) bereitstellt. Diese Tags sind dann ihrerseits wieder Interfaces, welche der Renderer kennen und unterstützen muss, z.B. Mesh, Material usw. Leider stellt sich hier aber ein ganz anderes Problem ein, das wäre aber Offtopic.
 
Vielen Dank für eure Antworten.

@tagedieb
Aber wenn das Renderable diese Information nicht halten sollte, wo sollte sie dann hingepackt werden?
Beispiel: VertexHandles für eine LWJGL-Implementierung. Jedes Renderable braucht ein VertexHandle. Wo und wie sollten die am besten gespeichert werden?
 
Spezifische Engine-Objects (z.B. LWJGL-VertexHandles) haben im RenderableObject nichts verloren. In Dem entgegengesetzte Aktionen vergangener Zeiten führten dazu, dass jedes 3D-API (JOGL, LWJGL, Java3D usw.) ihre eigenen Loader erwartet bzw. bereitstellt. Jeder kennt das: "Ich suche einen 3DS-Loader für Java3D, finde aber nur einen für LWJGL." Mal ehrlich, das kann doch nicht Stand der Dinge sein und ist der eigentliche Grund für meine schon oft erwähnte DataTypesLib - Diese würde höchstens einen 3DS-Loader (und nichts weiter) bieten, welche immer noch nicht veröffentlicht ist, weil es eben an dieser Verallgemeinerung von verschiedensten 3D-Objekt-Dateien krankt. Hab' deswegen schon 1000 Konzepte für die Tonne entwickelt, sprich verworfen. Ich hoffe, dass ich mit der TagList dieses mal richtig liege.
Das VertexHandle liegt im Renderer, welcher sich die Vertices aus dem Mesh-Tag des RenderableObjects zieht -> "Collection<Vertex> <Mesh (implements Tag)>.getVertices()".
 
Zuletzt bearbeitet von einem Moderator:
Deshalb wollte ich die Information (z.B. VertexHandles) ja mit einem Interfaces kapseln ...

VertexHandles im Renderer stelle ich mir fürchterlich unpraktisch vor, da ich ja irgendeine Zuordnung von Handle - Renderable brauche und der Renderer nur im Moment des Renderns Zugriff auf das Renderable bekommt.
Verticies liegen im Renderable-Objekt. Denke das macht auch Sinn.
 
Dachte auch mal, das das Sinn macht. Bei mir liegen sie jetzt in einem Mesh-Tag im RenderableObject. Der Grund dafür ist, dass ich LevelOfDetails implementieren wollte, was im Prinzip ja nichts anderes als Meshes mit geringerer Polygon- Vertexanzahl sind. Deswegen müssten LODs RenderableObjects mit all ihren anderen Attributen erweitern, was sie aber nicht sollten... (ein Teil, des oben erwähnten OT). LODs sollten nur an Mesh-Tags angehängt werden können und nichts weiter als Mesh-Attribute und eine Distanz o.ä. haben. Und jetzt geht die Verwirrung (bei mir) los... was ist mit Materialien und Texturkoordinaten? Wenn man das alles in Info-Tags hält und diese einem RenderableObject übergibt, statt sie eben dort zu implementieren, wie bekommt dann das RO Infos über evtl. Änderungen in seinen Tags mit, damit es seinerseits den Renderer darüber informieren kann? Evtl. Events?
 
Alle renderable-Objekte haben eine Informations-Objekt.
<->
Ich möchte die Zusatz-Information jedoch nicht direkt im "Renderable" haben

Das klingt widersprüchlich...

Die Andeutung von schalentier ist auf jeden Fall eine Erwägung wert: Das kann ein sehr allgemeines und mächtiges Konzept sein, und eine gute Trennung der Daten und ihrer Darstellung erlauben. Allerdings kann es da auch Grenzfälle und Pitfalls geben, wo es nicht so leicht ist, und falls ich das richtig verstanden habe, ist das hier genau so einer: NUR für das Rendern werden zusätzliche Daten benötigt, die einerseits spezifisch für jedes einzelne Objekt sind, andererseits aber auch spezifisch für den jeweiligen Renderer.

Wenn man versuchen würde, das in Kategorien einzuteilen, gäbe es die Fälle
- dass man Objekte hat, die direkt verarbeitet (gerendert) werden können
- dass mit jedem Objekt bestimmte Daten assoziiert sind, die NUR für die spezielle Verarbeitung benötigt werden.
Das bisher beschriebene klingt nach einer Mischung aus beidem.

GANZ theoretisch betrachtet könnte man sowas wie IAdaptable in Betracht ziehen: EclipseZone - What Is IAdaptable? - Aber ich hatte schon ähnliche Fälle, wo es aussah, als sollte das eigentlich passen, aber irgendwie zwickt und zwackt es dann doch überall...
 
> Zu den "widersprüchlichen" Zitaten: Gemeint ist in beiden Fällen eine Kapselung der Information. Dadurch haben die Renderables natürlich immer noch die Information.

>
Die Andeutung von schalentier ist auf jeden Fall eine Erwägung wert: Das kann ein sehr allgemeines und mächtiges Konzept sein, und eine gute Trennung der Daten und ihrer Darstellung erlauben. Allerdings kann es da auch Grenzfälle und Pitfalls geben, wo es nicht so leicht ist, und falls ich das richtig verstanden habe, ist das hier genau so einer: NUR für das Rendern werden zusätzliche Daten benötigt, die einerseits spezifisch für jedes einzelne Objekt sind, andererseits aber auch spezifisch für den jeweiligen Renderer.

Wenn man versuchen würde, das in Kategorien einzuteilen, gäbe es die Fälle
- dass man Objekte hat, die direkt verarbeitet (gerendert) werden können
- dass mit jedem Objekt bestimmte Daten assoziiert sind, die NUR für die spezielle Verarbeitung benötigt werden.
Das bisher beschriebene klingt nach einer Mischung aus beidem.

Das trifft es sehr sehr gut.
 
Ich wuerd mal noch anmerken, dass man im Allgemeinen aufpassen sollte, das Abstrahieren nicht zu uebertreiben. Ich weiss ja net, was genau du machen willst, aber imho ist es in einer Anwendung furchtbar selten, dass man die benutzte LowLevelRenderschicht (LWJGL,JOGL,J3D) austauscht. Bei einer Library oder einem Framework ist das was andres, aber ein konkretes Spiel wuerde ich immer fuer genau einen Renderer bauen. Ansonsten ist das irgendwie so aehnlich, wie wenn man noch ein Layer ueber JPA baut, damit man das austauschen koennte (hab ich wirklich schon in einem produktiven Projekt gesehen... 😵).
 
Ich wuerd mal noch anmerken, dass man im Allgemeinen aufpassen sollte, das Abstrahieren nicht zu uebertreiben. Ich weiss ja net, was genau du machen willst, aber imho ist es in einer Anwendung furchtbar selten, dass man die benutzte LowLevelRenderschicht (LWJGL,JOGL,J3D) austauscht.
Objektformate gehören zu keinen speziellen APIs. Für 3DS sollte es genau einen Loader geben, der für alle APIs verwendbar ist. 3D-APIs sollten keine Engines sein, in denen man nur die Formate verwenden kann, für die es auch einen Loader gibt. Das Trennen von Dateien und API ist definitiv nicht immer nur sinnvoll, sondern aus meiner Sicht schlicht ein Muss.
 

Zurück
Oben