Hallo, ich wollte mal wissen was in folgender situation geigneter ist.
ich weiss ich hatte schon mal einen Thread über Interfaces gestartet, aber hier gehts halt wirklich
um die Frage was würde ein “Profi” hier eher benutzen.
Es geht um 2D spiele / bzw programme.
Ich habe im letzten jahr ungefähr 174829 verschiedene Tutorials gelesen / geguckt, und dadurch sehr viel gelernt -
aber es kommen halt immer mehr Fragen auf, wenn verschiedene Leute verschiedenes verschieden erklären…
Nehmen wir mal dieses Beispiel mit der Savanne, von dieser Isy. Ich wollte das selbe mal nachprogrammieren, aber halt komplexer -
mit Vögeln die Elefanten fressen und so nem zeug. Wenn ich jetzt ein package habe mit paint, wo halt alles gezeichnete drin ist, dann habe ich dort
ja zB die Klassen Elefant und Vogel, Diese Erben von Tier. Und diese Klasse Tier, Die kann entweder:
von einer Klasse Painting erben, die eine Methode draw(Graphics g); besitz, dessen parameter von der paintComponent
der Start Klasse übergeben wird. Das heisst Elefant und Vogel erben von Tier und Tier erbt von Painting.
oder aber ich kann ein Interface kreiren, welches die Methode draw(Graphics g); anbietet. Das heisst Tier oder Pflanze würde nicht
Painting erben sondern das Interface Paintable Implementieren.
Warum mir das an dieser Stelle unnötig scheint: Wenn jetzt sowohl jede Pflanze als auch jedes Tier nach dem selben Zeichenschema funktioniert, (sagen mir mal einfach nur zeichne img),
dann müsste ich genau die selbe draw Methode in Pflanze und Tier hinschreiben. wohingegen ich bei einer Vererbung von Painting dort in der Regel die Methode nur einmal geschrieben wird:
und das Vererben an dieser Stelle - ist das logisch, oder so wie der Entwickler von Java sagte “ausnutzen” von vererbung?
Tut mir leid das das jetzt so lang geworden ist, aber ich habe halt mal meine Gedanken notiert.
(Ja ich bin mir bewusst dass das hier kein Blog ist :o) )
Also auf jeden Fall kann es schädlich sein, ein einziges Graphics-Objekt durch alle Instanzen sichtbarer Game-Objekte zu treiben, besser wäre, wenn man ein Graphics-Object hat, welches die Methode “drawGameObject()” kennt bzw die Game-Objekte Instanzen von RenderedImage wären.
Das Ganze aus dem Grund, weil die Grösse des Graphics-Objektes nur mittelbar (über “getClipRect()”) ermittelt werden und evtl. auch vom Game-Objekt verändert werden kann. GameObjekte könnten sich so in Playfield-Bereiche zeichnen, in denen sie eigentlich gar nichts verloren haben. Darüber hinaus könnten Game-Objekte den Graphics-Context durch den Aufruf seiner “dispose()”-Methode für alle folgenden Objekte ungültig machen. Eine Mthode, die ein Graphics-Objekt “durchreicht”, sollte demnach also etwa so aussehen:
void paint(Graphics g);
Rectangle getBounds(Rectangle bounds);
}
class Painting implements Paintable {
private int x, y, width, height;
public void paint(Graphics g) {
// do local paints
Rectangle bounds = null;
for(Paintable p : this) {
bounds = p.getBounds(bounds);
Graphics child = g.create(bounds.x, bounds.y, bounds.width, bounds.height);
p.paint(child);
child.dispose();
}
}
public Rectangle getBounds(Rectangle target) {
if(target == null) {
target = new Rectangle();
}
target.x = x;
target.y = y;
target.width = width;
target.height = height;
return target;
}
public Iterator<Paintable> iterator() {
return new Iterator<Paintable>() {
public boolean hasNext() {
return false;
}
public Paintable next() {
throw new NoSuchElementException();
}
public void remove() {
}
}
}
}```Der Iterator dieses Paintings ist natürlich noch leer. Aber man kann nun noch Methoden für das Hinzufügen und/oder Entfernen von Kindobjekten implementieren, diese in einer Collection speichern, deren Iterator man letzendlich zurück gibt. Wichtig hierbei ist aber auch mehr die Art, wie das Graphics-Objekt "durchgereicht" wird. Das Iterable soll dabei nur symbolisieren, das Kindobjekte existieren können.
Hm. Das mit dem Graphics war wohl nicht die Frage… und hatte auch nur SEHR indirekt damit zu tun. Aber … nebenbei: Genau für sowas wollte ich mal Tests machen, wie es da mit der Performance aussieht. Ich weiß nicht, wie “leichtgewichtig” das Erstellen eines Kind-Graphicss ist. Allgemein hat man ja mehrere Möglichkeiten, von “ignorieren der Frage” über Manuelles “Pushen/Poppen des Zustands” bis zum Child-Graphics. Als Extrembeispiel: Macht es Sinn, bei 10000 “LinePainter”-Objekten mit
wirklich 10000 Graphics-Objekte zu erstellen, oder sollte man einfach antizipieren, dass NACH dem ausführen eines LinePainters die Color vielleicht eine andere ist, als vorher?
Wie auch immer, zur eigentlichen Frage: Erstmal würde ich die Methode in Interface packen, und möglichst wenig in eine (“echte”, d.h. aus Klassen bestehende) Vererbungshierarchie reinpacken. Falls die “draw”-Methode bei allen Implementierungen gleich ist, kann man sich was überlegen.
Eine Möglichkeit fehlt noch: Neben class Tier extends Painting { void paint(...); } und class Tier implements Paintable { void paint(...); }
gäbe es noch
interface Painter<T> { void paint(T t, Graphics g); }
class ElephantPainter implements Painter<Elephant> {
@Override
public void paint(Elephant e, Graphics g) {...}
}
[QUOTE=Marco13]Hm. Das mit dem Graphics war wohl nicht die Frage… und hatte auch nur SEHR indirekt damit zu tun…[/QUOTE]Du immer mit deinem “kritisch hinterfragen…” :lol:
Wann immer ein Graphics-Objekt über Kindobjekte verteilt werden soll, macht es definitiv Sinn, dieses zu kapseln, damit es, wenn innerhalb eines Kindobjekts jemand auf die Idee kommt, das Graphics-Objekt zu “disposen” (was btw nicht empfohlen wird), nicht zu unerwarteten Fehlern kommt. Besonders teuer ist das nicht, weil sich die einzelnen Graphics-Objekte die Zeichenoberfläche ja teilen und maximal nur den durch “bounds” zugeteilten Bereich verändern können. Anders sieht das aus, wenn mach schlicht die Clip-Bounds verändert und die selbe Instanz weiterreicht, innerhalb des Kindobjektes können diese Clip-Bounds nämlich wieder auf Maximalwerte (maw ausserhalb des vorgegebenen Bereichs) gesetzt werden. Dagegen hilft auch so’n “Painter” nichts, ganz einfach, weil ein Graphics-Objekt mehr oder weniger “Freiwild” ist.
Was aber wie gesagt noch ginge (und ab Java7 sogar noch viel besser, weil man nicht mehr aufs SunWritableRaster beschränkt ist):