Methoden in enums, die kopie einer variable zurückgeben?

ich muss aber zu Vector3f casten können, um den vektor weiterzuverwenden…

also, egal wie man es dreht ich brauche unbedingt eine kopie. ich brauche einen Vektor3f mit den werten von vector den ich anschließend verändere.

WO musst du WARUM nach Vector3f casten? Will/soll derjenige den Vector verändern können? Genau daher stammt ja das Problem: Wenn man die Referenz rausgibt, könnte ihn jemand verändern, deswegen eine „defensive copy“. Wenn derjenige den Vector verändern will, muss es ein veränderbarer Vector sein. Ansonsten … lehne ich mich mal weit aus dem Fenster, und behaupte, dass das klingt, als würde da jemand einen Vector3f erwarten, der eigentlich nur einen ReadableVector3f erwarten sollte :wink:

ich rufe dort wo ich was bewege folgendes auf:

position.translate((Vector3f) moveDirection.getVector().scale(speed));

darum gehts.

@TT:

farbe.malen(); //nicht ok```

besser (erklärung) ?

Ja, da kann eben keine Referenz auf das veränderbare Objekt zurückgegeben werden. (<-Punkt)
[ot]

Mini-Rant:

Die Modellierung der Vector-Klassen in LWLJG ist etwas fragwürdig. Ich weiß, es ist schwer. Genaugenommen habe ich schon SEHR oft SEHR lange darüber nachgedacht, wie man solche Klassen für (niedrigdimensionale, <=4D) Vektoren gut designen könnte. Jeder Punkt, bei dem man sagen könnte: „Das-und-das ist nicht gut, das sollte man eher so-und-so machen“, hat Folge, dass jemand sagen kann „So-und-so hat man aber in diesem-und-jenem Fall einen Nachteil“. Aber in bezug auf LWJGL:
[ul]
[li]Dass die Vektoren voneinander erben (3D erbt von 2D) bringt Barbara Liskov zum Weinen[/li][li]Dass (teilweise daraus folgend) Methoden wie „Vector3f#scale“ einen „Vector“ liefern (und keinen Vector3f) ist häßlich[/li][li]Dass Methoden wie [/li]static Vector3f add(Vector3f left, Vector3f right, Vector3f dest)
nicht
static Vector3f add(ReadableVector3f left, ReadableVector3f right, WritableVector3f dest)
heißen, führt die (ansonsten gar nicht so verkehrte) Unterscheidung von „Readable“ und „Writable“ ziemlich ad absurdum…
[li]… (weitere, ist Off-Topic…)[/li][/ul]

[/ot]

In diesem Fall wäre die IMHO sauberste Lösung (soweit unter den gegebenen Vorbediungungen möglich) grob sowas:

public enum Direction {

    // Wie vorher, aber hier den Vector direkt (und nur als ReadableVector) zurückgeben:   
    public ReadableVector3f getVector(){
        return vector;
    }
}

Verwendung

void applyMovement(Direction moveDirection, float speed) 
{
    position.translate(scaled(moveDirection.getVector(), speed));
}

private static Vector3f scaled(ReadableVector3f in, float scale) {
    Vector3f result = new Vector3f(in);
    result.scale(scale);
    return result;
}

Nun mag man (zu Recht!) sagen, dass damit ziemlich viel Garbage entsteht: Da werden u.U. SEHR viele Vector3f-Instanzen erstellt, und der Garbage Collector könnte sich da leicht dran verschlucken.

EDIT Was aber auch bei der ursprünglich skizzierten Lösung mit der defensive copy der Fall wäre! < EDIT
Das könnte man vermeiden, FALLS es wirklich ein Problem ist, etwa so:

private final Vector3f scaledMovement = new Vector3f();

void applyMovement(Direction moveDirection, float speed) 
{
    scale(moveDirection.getVector(), speed, scaledMovement)
    position.translate(scaledMovement);
}

private static void scale(ReadableVector3f in, float scale, WritableVector3f out) {
    out.set(in.getX() * scale, in.getY() * scale, in.getZ() * scale);
}

sollte sich aber im Klaren sein, dass das bei Multi-Threaded Aufrufen von „applyMovement“ (FALLS die denn auftreten können, was eher unwahrscheinlich ist), ein Problem sein könnte.

[QUOTE=mymaksimus]ReadbleVector3f v = new Vector3f(); ((Vector3f) v).x = ....

räusper[/QUOTE]

Wenn du damit andeuten willst, dass ReadableVector3f nichts bringt, weil man mit einem Cast ja doch an die Member herankommt, liegst du völlig falsch:

Durch die Wahl von ReadableVector3f in einer API wird die Absicht der Unveränderlichkeit klar dokumentiert. Wer das mit einem Cast hintertreibt, ist dann halt selber schuld, wenn es kracht - genauso, wie jemand, der über Rawtypes Generics ignoriert, sich per Reflection Zugriff auf private Member verschafft oder mit Bytecode-Manipulation eine Klasse auf links krempelt. Java bietet auf diesen Gebieten keine absolute Sicherheit, deshalb sind alle diese Vorkehrungen ein guter Schutz gegen unabsichtliche Falschbenutzung, nicht gegen böswilligen Mißbrauch.

gut, das macht einiges klarer… danke ^^
nicht das ich das noch nie aufgeschnappt hätte, aber jetzt ist es gut in meinem bewusstsein verankert ^^

[QUOTE=mymaksimus]ich rufe dort wo ich was bewege folgendes auf:

position.translate((Vector3f) moveDirection.getVector().scale(speed));

darum gehts.[/QUOTE]```position.translate(moveDirection,speed);

//class Position
public void translate(Direction moveDirection, Number speed){
this.internalPosition = moveDirection.move(internalPosition, speed);
}```Vorteil: keine Abhängigkeiten zu einer der Implementierungen der Vektoren außerhalb der Klassen Position und Direction und auch keine casts -> Lesbarkeit verbessert.

bye
TT

keine Casts weil die Methode, in der tatsächlich gerechnet wird, ausgeblendet ist,

durchaus nicht falsch komfortable Hilfsmethoden zu haben, einen Vector-Hilfsrechner, aber wieso sollte ausgerechnet die Enum-Klasse das sein?
es gibt womöglich noch zig andere Stellen wo auch mit Vectoren gerechnet wird, Enum unbeteiligt

nach direction.moveFrom(this.position); wäre das hier schon die zweite Vector-Berechnungsmethode in der Enum, wieviele noch?,
soll jede beliebige Berechnung mit 1, 2, 12 Parameter immer in der Enum ausgeführt werden so dass nur das Endergebnis der Variablen zugewiesen wird?


ein anschaulicheres Beispiel als Enum und Vector ist vielleicht x-Beliebige Pojo-Klasse und String,
eine Methode String getName() oder ähnlich kann in einem Projekt bei zig Klassen vorkommen, Person, Stadt, Bank, Gesetz, DatenX, DatenY, DatenZ

soll jede dieser Klasse wiederholend komplizierte Methoden wie getNameFormatted(4 Parameter), calculateSubStringSearchForEmptySpaceConcerningYourName(), getLengthYourName() haben?

mit der Verrücktheit, dass diese Methoden dann genauso String oder einen Vector oder andere direkt abgeleitete Informationen liefern,
der Name oder Vector der Enum sind ja auch keine Geheimnisse,


Woche für Woche nur Kopfschütteln über völlig verkorkste Vorschläge fern aller Realitäten…,

der Hinweis an sich ist wie so oft etwas wert, man KANN damit wie mit allen höheren Konzepten gutes bewirken,
aber das Denkverbot zu den einfachen Wegen, verrückt…

[quote=SlaterB]keine Casts weil die Methode, in der tatsächlich gerechnet wird, ausgeblendet ist,[/quote]Falsch, die Casts sind auch dort nicht nötig.

[quote=SlaterB;109429]wieso sollte ausgerechnet die Enum-Klasse das sein?[/quote]Weil die Bewegung von ihrer Richtung nicht zu trennen ist.

[quote=SlaterB;109429]es gibt womöglich noch zig andere Stellen wo auch mit Vectoren gerechnet wird, Enum unbeteiligt[/quote]Ja, dafür muss ich dort die Berechnung nicht nochmal hin schreiben.

[quote=SlaterB;109429]nach direction.moveFrom(this.position); wäre das hier schon die zweite Vector-Berechnungsmethode in der Enum[/quote]Mann kann sich die Sachen natürlich auch an den Haaren herbei ziehen, aber direction.moveFrom ist nicht sinnvoll, wenn es direction.move gibt.

Aber ja, wenn es verschiedene Möglichkeiten gibt, unter Berücksichtigung einer Richtung zu einer neuen Position zu gelangen benötige ich eben mehrere Methoden und zwar unabhängig davon wo die dann letztendlich stehen.
Und wenn ich es mir aussuchen kann dann packe ich das in die Kasse, ber der sich das am natürlichsten anfühlt. In diesem Fall wäre das für mich eben das Enum.

[quote=SlaterB;109429]ein anschaulicheres Beispiel als Enum und Vector ist vielleicht x-Beliebige Pojo-Klasse und String,[/quote]nein, ist es nicht.
Und verallgemeinern kann man solche Designentscheidungen auf der Ebene auf der Du das hier tun willst sowieso nicht. Das sind Einzelfallentscheidungen.

[quote=SlaterB;109429]Woche für Woche nur Kopfschütteln über völlig verkorkste Vorschläge fern aller Realitäten…,[/quote]Nur weil Du es nicht verstehst heist das nicht, dass es realitätsfern wäre.

[quote=SlaterB;109429]aber das Denkverbot zu den einfachen Wegen, verrückt…[/quote]Es geht doch nicht um ein Denkverbot.
Der Punkt ist, dass die “einfachen Wege” später Ärger machen. Das geht beim Thema “Testbarkeit” los und schlägt dann beim Thema “Änderbarkeit” voll zu. Diese Erfahrung darfst Du mir ruhig glauben.

KISS, DRY und “Command don’t ask” sind aus gutem Grund anerkannte Konzepte in der Programmierung. Irgendwann erkennst auch Du das…

bye
TT

Landei meint, es kann auf “illegalen” Wegen gemacht werden, sollte es aber “normalerweise” nicht, weshalb es für den normalen Gebrauch bestimmte ‘Schutzmechanismen’ gibt. private Member verändern/lesen, hab ich zwar noch nicht ‘wirklich’ probiert, aber man kann ja auch z. B. “zurückübersetzen”. Also kann, sollte nicht, + erschwert durch Umwege.

In der Theorie, Java ist eine sehr streng typisierte, aber nicht sehr sehr streng typisierte Programmiersprache, d. h. dazu zählen auch casts. Hoffe, hab’s richtig wiedergegeben.

popcorn raushol :o)

von ‚denn Getter sind immer erstmal böse…‘ zu ‚Einzelfallentscheidungen‘,
anscheinend ist mir das fast Unmögliche gelungen, ein Beispiel zu finden wo ein getter doch nicht so böse ist,

oder die entspannte Einstellung: getter sind vollkommen normal, in Einzelfällen aber raffinierte Umbauten denkbar, z.B. …

mehr zur Position-Klasse als zur Direction-Enum:
die Direction-Klasse hat aber nicht vollständige Bewegungsmacht,
wie wäre es herbeigezogen mit der Möglichkeit, dass es nicht nur Enum-Standard-Directions gibt sondern auch andere Vektoren für Positions-Translate,
z.B. NordOst aus zwei Enum-Werten kombiniert (ohne getter = eine neue Methode…, wobei man auch immer move mit 0,0 und Speed 1 aufrufen kann…),
oder Richtung nach Mausbewegung/ -klick auf Bildschirm oder oder,
eine zweite translate-Methode in Position? (welches hier freilich eh nur vages Beispiel ist)

[quote=Timothy_Truckle;109432]

Ja, dafür muss ich dort die Berechnung nicht nochmal hin schreiben.[/quote]
was mag ‚dort‘ sein?
dort = die anderen Stellen ohne Enum? inwiefern sollte dort etwas nicht hingeschrieben werden müssen wenn in der nicht beteiligten (!) Enumklasse etwas steht?
dort = die Enum? äh, dort wo schon etwas hingeschrieben wurde muss nichts nochmal hingeschrieben werden?

das klingt lockerer als auf gar keinen Fall zu erlauben dass den Vector (die global allen verfügbare unveränderliche Information) per getter aus der Klasse zu lassen (böse),

lustig dass dagegen von Position aus der private versteckte individuelle Vector ohne Not in andere Klassen verschoben wird, wobei

  this.internalPosition = this.internalPosition.add(moveDirection.move(speed));
}

natürlich noch eine Variante wäre, eine Stufe näher zum getter, nur auf speed = 1 zu kommen

[quote=SlaterB]oder die entspannte Einstellung: getter sind vollkommen normal, in Einzelfällen aber [raffinierte gestrichen TT] Umbauten denkbar[/quote] Ich sehe das eben anders: Getter sind mit Bedacht einzusetzen und nur, wenn eine andere Lösung unverhältnismäßig viel Aufwand bedeuten würde (oder die Klasse ein DTO ist).

Meist beläuft sich der Aufwand aber darauf, in der Klasse, in dem man auch den Getter erzeugen müsste, ein neue Methode anzulegen. Und ja: dann vermeide ich den Getter. “Tell, don’t ask” eben…

bye
TT

nochmal zu getVector3f(),
eine noch nicht genannte Variante ist, die Klasse zu überschreiben und in den set-Methoden Exceptions zu werfen,
sofern die Klasse nicht final ist,

steht dann freilich evtl. der Klassenbeschreibung entgegen,
vergleichbar mit Collections.unmodifiableList() usw.
wo man sich vielleicht dadurch abgesichtert hat, dass in der API steht, dass auch UnsupportedOperationException fliegen kann…

geht nicht, falls man eine freie Kopie erwartet, diese aktiv bearbeitet bei Gelegenheit,
oder sie als Parameter beliebig herumreicht wo Bearbeitung möglich ist, Quelle nicht mehr zu unterscheiden,

wenn man derartiges will, dann muss es wohl Kopie sein,

wenn man sich in der Nutzung zusammenreißt, nur daraus lesen oder passiver Berechnungspartner,
dann kann man auf diese Weise auf neue Vector3f verzichten,
für eine Enum mit sparsamer Beständigkeit als Haupteigenschaft ein nettes Ziel