GUI und Backend - Zyklenproblem

Status
Nicht offen für weitere Antworten.

Rock Lobster

Bekanntes Mitglied
Servus,

ich würde von euch mal gerne wissen, wie ihr das folgende Problem löst:

Nehmen wir an, wir haben eine GUI mit ein paar Schiebereglern, und wir haben im Backend irgendetwas, was durch diese Schieberegler gesteuert werden kann. Sagen wir einfach mal, einer dieser Regler heißt "Lautstärke". Wenn diese geändert wird, wird das GUI-Event ausgewertet und dann im Backend eine setVolume()-Funktion aufgerufen.

Soweit, sogut. Aber unter Umständen kann es ja auch wünschenswert sein, daß die Lautstärke programmatisch gesteuert wird, z.B. weil jemand ein Config-File einliest. Wenn nun setVolume() aufgerufen wird, soll ja wiederum auch in der GUI der Schieberegler auf die richtige Position eingestellt werden.

Dies würde nun aber wieder das GUI-Event auslösen, welches wiederum die Lautstärke setzt, und dieses Lautstärkesetzen würde wieder die GUI aktualisieren, und dies würde nun aber wieder das GUI-Event auslösen, welches wiederum die Lautstärke setzt, und dieses Lautstärkesetzen würde wieder die GUI aktualisieren, und dies würde nun aber wieder das GUI-Event auslösen, welches wiederum die Lautstärke setzt, und dieses Lautstärkesetzen würde wieder die GUI aktualisieren, und dies würde nun aber wieder das GUI-Event auslösen, welches wiederum die Lautstärke setzt, und dieses Lautstärkesetzen würde wieder die GUI aktualisieren, und dies würde nun aber wieder das GUI-Event auslösen, welches wiederum die Lautstärke setzt, und dieses Lautstärkesetzen würde wieder die GUI aktualisieren, und dies würde nun aber wieder das GUI-Event auslösen, welches wiederum die Lautstärke setzt, und dieses Lautstärkesetzen würde wieder die GUI aktualisieren, und dies würde nun aber wieder das GUI-Event auslösen, welches wiederum die Lautstärke setzt, und dieses Lautstärkesetzen würde wieder die GUI aktualisieren, und dies würde nun aber wieder das GUI-Event auslösen, welches wiederum die Lautstärke setzt, und dieses Lautstärkesetzen würde wieder die GUI aktualisieren, und dies würde nun aber wieder das GUI-Event auslösen, welches wiederum die Lautstärke setzt, und dieses Lautstärkesetzen würde wieder die GUI aktualisieren, und dies würde nun aber wieder das GUI-Event auslösen, welches wiederum die Lautstärke setzt, und dieses Lautstärkesetzen würde wieder die GUI aktualisieren, und dies würde nun aber wieder das GUI-Event auslösen, welches wiederum die Lautstärke setzt, und dieses Lautstärkesetzen würde wieder die GUI aktualisieren, und dies würde nun aber wieder das GUI-Event auslösen, welches wiederum die Lautstärke setzt, und dieses Lautstärkesetzen würde wieder die GUI aktualisieren, und dies würde nun aber wieder das GUI-Event auslösen, welches wiederum die Lautstärke setzt, und dieses Lautstärkesetzen würde wieder die GUI aktualisieren, und dies würde nun aber wieder das GUI-Event auslösen, welches wiederum die Lautstärke setzt, und dieses Lautstärkesetzen würde wieder die GUI aktualisieren, und dies würde nun aber wieder das GUI-Event auslösen, welches wiederum die Lautstärke setzt, und dieses Lautstärkesetzen würde wieder die GUI aktualisieren, und dies.... ihr wißt, was ich meine :autsch:


Natürlich kann man dies verhindern. Zum Beispiel indem man beim Lautstärke-setzen immer dasjenige Objekt mitübergibt, welches das Kommando gesendet hat, um dann zu überprüfen, ob man kurz davor ist, solch einen Zyklus auszulösen. Oder man kann zwei verschiedene Funktionen machen, eine setVolume() und eine setVolumeFromGui(), aber das ist auch nicht gerade schön. Oder man könnte mit den Funktionsaufrufen aufhören, wenn man bemerkt, daß sich der Wert gar nicht geändert hat. Aber das ist schon wieder sehr speziell irgendwie...

Wie löst man das "richtig"?
 

SnooP

Top Contributor
hmm - da gibt's natürlich wieder viele Möglichkeiten - aber generell könnte man ja einfach auf ein PropertyChange Event hören - sprich es wird nur geändert, wenn sich der Wert tatsächlich ändert - sonst wird auch das event nicht gefeuert und es tut sich gar nicht erst was. Damit verhindert man dann auch, dass zu viel Kommunikation stattfindet.
 

tfa

Top Contributor
Ich würde ein Flag benutzen, das während der programmatischen Änderung gesetzt ist.
Wenn es gesetzt ist werden GUI-Events ignoriert. Du musst nur sicherstellen, dass es auf jeden Fall zurückgesetzt wird (also try-finally verwenden).
 

Marco13

Top Contributor
Hm. Die beiden wichtigsten Lösungen wurden schon angedeutet. Eine davon von dir selbst :wink: Aber ... das Objekt mit zu übergeben, das das Kommando gesendet hat, ist nicht so schön. Wer weiß, was der Empfänger damit macht...

Nochmal:

1. Man kann ein Flag verwenden:
Code:
class GUI
{
    private boolean updating = false;

    void sendInfoAboutVolumeChange()
    {
        updating = true;
        model.setVolume(x); // Löst über umwege aus, dass recieveInfoAboutVolumeChange aufgerufen wird
        updating = false;
    }

    void recieveInfoAboutVolumeChange()
    {
        if (updating)
        {
            System.out.println("Jaja, ich weiß, lass mich in Ruhe...");
        } 
        else
        {
            slider.setValue(model.getVolume()); // Löst über umwege aus, dass sendInfoAboutVolumeChange aufgerufen wird
        }
    }

}

2. Was eigentlich "sauberer" oder wünschenswerter ist: Es passiert das, was zu dem passt, was passiert ist :wink:
Wenn man im Programm die Lautstärke ändert, wird ja evtl. irgendein "VolumeChanged"-Event geworfen, der dann vom GUI aufgefangen wird, und es wird der Slider entsprechend angepasst. Aber... wenn sich die Lautsärke NICHT ändert, sollte auch kein VolumeChanged-Event geworfen werden...
Code:
void setVolume(int newVolume)
{
    if (this.volume == newVolume)
    {
        System.out.println("Nix zu tun...");
    }
    else
    {
        this.volume = newVolume;
        fireVolumeChangedEvent(becauseItREALLYChanged);
    }
}
Warum das "sehr speziell" ist, oder warum es "schlecht" sein sollte, dass es "sehr speziell" ist, weiß ich nicht. Viel interessanter ist die Frage, ob man es schafft, dieses Paradigma, nur Events zu werfen, wenn sich etwas geändert hat, so konsequent durchzuziehen, dass man sich im GUI 100% darauf verlassen kann...
 

quippy

Bekanntes Mitglied
Der setVolume setzt doch die Lautstärke im Programm selbst -also z.B. speichert es den Wert in einer Variablen im Middle-Tier. Da diese Methode nix von einer GUI wissen sollte, darfst Du den Wert da auch gar nicht entsprechend an eine GUI transportieren.

Wenn nun aus einer Property-Datei eine Lautstärke gesetzt werden soll, dann würde ich das anders behandeln:

In der GUI hast Du einen ChangeProperty-Event an den Lautstärke-Regler gebunden, der dann in die Middle-Tier verweist und das eben angesprochene setVolume anspricht.

Die Property-Datei wird auch von der GUI geladen (bzw. von einer Klasse, welche die GUI kennt) und verwendet dann das setProperty (oder wie die Methode da heißt) des Schiebereglers - d.h. die Property-Datei setzt nicht den Wert in der Middle-Tier sondern verändert die GUI wie es der Benutzer auch tun würde. Über das ChangeEvent wird dann automatisch der neue Wert verteilt.
 

Rock Lobster

Bekanntes Mitglied
Naja das mit der Property-Datei war jetzt aber nur ein Beispiel. Kann ja auch sein daß sonstwer meinem Backend sagen will, daß sich die Lautstärke zu ändern hat, und das soll natürlich auch sofort in der GUI sichtbar sein. Natürlich spricht das Backend die GUI nicht direkt an, sondern über einen entsprechenden Listener, aber das Problem besteht dann eben weiterhin, weil die GUI daraufhin ihren Regler ändert und dieser nun wieder ein Event auslöst...

Vom Prinzip her gefällt mir Marco13s zweite Lösung am besten, aber es ist halt in der Tat schwer, das "durchzuboxen". Daher hab ich gehofft, es gibt vielleicht eine Lösung, die wasserdicht ist und bei der man nicht viel falsch machen kann...
 

quippy

Bekanntes Mitglied
Wenn Du möchtest, daß Deine GUI immer aktuell ist und damit also im Prinzip von der Middleware informiert werden soll (also auch z.B. per Listener-Konzept lösbar), dann ist das echt etwas Tricky.

In Swing (wo ja bekanntliche viele Listener rumsenden) werden immer dann keine Events gesendet, wenn sich eine Property nicht ändert. Daher heist das Event ja auch PropertyChange und nicht PropertySet - man feuert nur, wenn sich was geändert hat.

Das bedeutet, jemand ruft setVolume auf, welches die GUI informiert, daß es geändert wurde. Der PropertyChange feuert nun zu setVolume zurück, da es tatsächlich eine Änderung war, wobei setVolume prüft, ob ein neuer Wert kommt und dann selbst nicht zurück feuert.
Falls doch würde in dem Augenblick der PropertyChangelistener kein zweites mal senden, da ja der selbe Wert zurück kommt.

Soweit verständlich? Von Flags würde ich wegen möglicher Nebenläufigkeit Abstand nehmen!
 
Status
Nicht offen für weitere Antworten.

Ähnliche Java Themen

Neue Themen


Oben