Java [Swing] Best Practices - Observer, PropertyChangeListener, uvm

#basTi

Commodore Pro
Registriert
Aug. 2005
Beiträge
4.763
Hallo zusammen,

ich bin gerade dabei mein erstes größeres Projekt zu realisieren und ich habe noch einige Fragen.
Es geht um ein Programm für den produktiven Einsatz, das eine recht simple Logik hat und ein GUI, welches aus dem Hauptfenster besteht + mehrere Dialogfenster (Einstellungen, etc) + Alert Dialoge.

Soweit zum ungefähren Umfang.
Umgesetzt wird es nach MVC-Pattern.


Nun zu meinen Fragen:
  • Ich lese viel zum Thema Notifications, d.h. wer benachrichtigt / horcht auf wen.
    Nur was ist die elegantestev(find grad kein besseres Wort ;) ) Methode dies Umzusetzen? Die einen sagen, blos kein Observer / Observable benutzen sondern ChangeListener / PropertyChangeListener. Dann gibts natürlich noch weitere Alternativen, welche diskutiert werden ...
    Was ist eurer Meinung das Optimum?
  • Wie geh ich am besten mit Alert Dialogen um? D.h. wenn zum Beispiel ne Exception kommt. Wie löse ich das intern am elegantesten? Gibt es dort Best Practices?

Am wichtigsten ist mir persönlich der erste Punkt: Wie schaut das "Benachrichtigungs-System" aus.
Natürlich weiss ich, dass es nicht "DIE beste Lösung" gibt aber für ein mittelgroßes Swing Programm wie hier beschrieben hätte ich gern ein paar Ideen / Meinungen. :)

Schon mal vielen Dank für eure Antworten :)
 
Ganz einfach: Das ursprüngliche Event-System von Java mit Observable als Klasse usw ist "doof". Es ist sinnvoller, dafür Interfaces zu verwenden, wie bei ChangeListener.

Es gibt auch unterschiedliche Arten, das Observer-Pattern umzusetzen... so wird häufig nur mitgeteilt, dass sich irgendwas geändert hat. Ich bevorzuge die Variante, bei der die Änderungen direkt mitgeteilt werden im Event.

Und zur zweiten Frage: Wie und ob eine Exception behandelt werden sollte, hängt sehr stark von der Exception selbst ab...
 
Schonmal danke für deine Antwort :)

Kannst du mir ein Minimalbesipeil geben für deine Lösung in Bezug auf MVC geben?
 
Noch was allgemeines von mir, weil ich im Moment leider keine Zeit habe, über Architekturdetails nachzudenken:

Was immer wieder gern vergessen wird, ist GUI-Kram immer in den Swing-Thread zu queuen (SwingUtilities.invokeLater) und andere Logik (zumindest komplizierteren Kram) nicht in diesem Thread auszuführen (bspw. per SwingWorker).

Das kann man direkt ausnutzen, um Logik von Darstellung sauber zu trennen.
 
Nimm dir das was character sagt unbedingt zu Herzen. Vor allem der SwingWorker wird dir sonst viele, berechtigte!, Diskussionen und Probleme ersparen, die vor allem bei "simplen" Programmen dem unerfahrenen Programmierer erst zur Laufzeit im Live Betrieb bekannt werden ;)
 
Warum bestehst Du auf das MVC Pattern? Das wird heutzutage eher als Anti-Pattern gehandelt. ;) Sehr viel besser ist das MVP Pattern, da dadurch eine strikte Trennung und lose Kopplung von Model und View erzielt bzw. erzwungen wird (weder View noch Model kennen sich, der Presenter fungiert als Delegate der View und nur der kennt das Model).

Zwischen Presenter und Model implementierst du dann das ganz normale Observer-Pattern. Die View ist komplett passiv und wird durch den Presenter aktualisiert.
 
Der Nachteil von Observer / Observable ist doch, dass ich ständig casten muss?
So habs ich zumindest verstanden und die ChangeListener sollen in dieser Hinsicht besser sein.
Oder bin ich auf dem falschen Dampfer? ;)
 
Warum muss man beim Observer casten? (und was?)

Aber den Observer kann man generell ganz schön vergewaltigen wenn man will ;-)
 
Ich redete vom Observer-Pattern, nicht verwechseln mit der Observer-Klasse.
Das Observer-Pattern kannst du selbst implementieren. Alles was du brauchst ist eine abstrakte Modelbasisklasse, die das an-/abmelden und die Notifikation von Listenern implementiert. Konkrete Models können dann noch detailiertere Events bereitstellen. Um sicherzustellen, dass Model und Listener zusammenpassen, sollten die Listener für jedes konkrete Model ein eigenes Interface implementieren.
 
Schonmal danke für all die Antworten!

Ich versuch mal ein Codebeispiel zu geben, welche die Funktionalität demonstrieren soll, die ich mir wünschen würde.

Code:
// Model Klasse
...
NotificationCenter.sendNotification(NotificationsEnum.Customer_Added, customer);
...

// View Klasse
...
NotificationCenter.registerAsListener(NotificationsEnum.Customer_Added, this.handleNewCustomer);
...
public void handleNewCustomer(Customer customer){
...
}
Wahrscheinlich gibt es noch elegantere Lösungen aber diese ist mir auf Anhieb eingefallen ;)
 
Und was hälst du davon?

800px-Observer.svg.png
 
Nicht so viel, weil:
- es keine Unterscheidungen zwischen Notifications gibt und dadurch alle Observer "angestupst" werden.
- das aktualisierte Objekt nicht mitgegeben wird sondern geholt werden muss.
 
Das ist ein Pattern - warum willst du das nicht an deine Bedürfnisse anpassen?
Da noch etwas mitzugeben ist doch jetzt wirklich nichts schweres.. Ich sehe es zwar selten aber mir fällt auch kein Grund ein warum du nicht in der Nachricht ein Objekt mitgeben könntest. Es muss ja nur zum Interface passen.

Das alle Beobachter benachrichtigt werden, macht ja Sinn. Wenn die keine Infos brauchen, dann brauchen sie auch nicht angemeldet zu sein !?
Im schlimmsten Falle vergewaltigst du das teil und baust dir unterschiedliche Observer Interfaces und musst beim benachrichtigen noch prüfen. Das wäre aber für mich aus Software Ergonomie absoluter Fail.
 
Von anpassen war doch nicht die rede :) Du hast nur gefragt, was ich konkret davon halte.

Das alle Beobachter benachrichtigt werden finde ich nicht so optimal...ich erkläre auch gleich warum. Aber falls du bessere Argumente hast lass ich mich auch überzeugen.
Also ich finde, dass man auf mehrere Events/notifications hören können sollte, weil manche Beobachter nur an bestimmten Updates interessiert sind. Wenn jeder bei jedem Event angestupst wird ist das mMn höchst ineffektiv.
(mein mockup Code oben sollte das ja verdeutlichen)
 
Ja aber genau das kannst du mit dem Pattern doch umsetzen. Du meldest dich bei den Subjekten an, von denen du benachrichtigt werden willst. Und bei den anderen eben nicht.
Dementsprechend verpflichte ich mich, die Interfaces der entsprechenden zugehörigen Beobachter zu implementieren, sonst kann ich ja nicht aufgerufen werden.

Ich weiß nicht ob wir aneinander vorbei reden, oder ich dein Problem einfach nicht erkenne?
 
Naja wenn ich das richtig versteh, dann ist folgendes bei deinem nicht möglich:
Eine klasse, die mehrere notifications von sich gibt. Z.B eine Model klasse die 2 oder mehrere Updates macht ( customer_added, customer_removed, ...)

Bei deinem Pattern kann man nur je klasse unterscheiden.
Man bräuchte also bei dem vom mir gepostetn Code einde Map mit (notificationTyp -> Funktion) wobei das mit Funktionen in Java ja schwer wird oder? Alternativ evtl pro Action eine eigene klasse ?..

Und man müsste noch Casten, falls man ein Objekt übergibt ... In jedem der beispiele. Dafür is mir aber auch noch keine elegante Lösung eingefallen. Evtl gibts auch keine ;)
 
Ausgehend vom Post #10 müsste dein Listener-Interface etwa so aussehen:
Code:
interface CustomerListener {
	public void handleNewCustomer(Customer customer);
	public void handleCustomerRemoved(Customer customer);
}
D.h. deine View-Klasse muss beide Methoden implementieren, auch wenn sie sich nur für erstere angemeldet hat. Also brauchst du keine Abfrage, wer sich für was registriert hat, sondern rufst bei allen Listenern die entsprechende Methode auf. Wenn ein Listener diese nicht braucht, lass sie einfach leer.
 
`basTi schrieb:
Naja wenn ich das richtig versteh, dann ist folgendes bei deinem nicht möglich:
Eine klasse, die mehrere notifications von sich gibt. Z.B eine Model klasse die 2 oder mehrere Updates macht ( customer_added, customer_removed, ...)

Bei deinem Pattern kann man nur je klasse unterscheiden.
Man bräuchte also bei dem vom mir gepostetn Code einde Map mit (notificationTyp -> Funktion) wobei das mit Funktionen in Java ja schwer wird oder? Alternativ evtl pro Action eine eigene klasse ?..

Warum sollte ein Subjekt nicht mehrere Notifications abgeben können? Wo steht das? Das ist ein Pattern, und kein in Stein gemeiseltes Stück Code.. Du weißt doch was ein Pattern im Allgemeinen ist?

Für den zweiten Part hat schon jemand einen Vorschlag gepostet.

Und man müsste noch Casten, falls man ein Objekt übergibt ... In jedem der beispiele. Dafür is mir aber auch noch keine elegante Lösung eingefallen. Evtl gibts auch keine ;)

Nochmal die Frage: Was willst du denn als casten !?
Du übergibst ein Objekt. Warum willst du ein Objekt nochmal in etwas anderes casten?
Wenn ich das Objekt Auto brauche, dann übergebe ich auch das Objekt Auto dort. Wenn ich etwas anderes brauche, übergebe ich etwas anderes. Aber ich caste nicht vom LKW zum Auto oder sonstigem.. Und wenn dort so viele Allgemeine Objekte gibt die du eventuell aktualisiert rumreichen willst halte ich dein komplettes Architektur-Design für fehlgeschlagen und rate dir es gründlich zu überarbeiten! Starke Kohäsion - schwache Kopplung! Das wirkt mir dann solangsam mehr wie ein Blob.
 
Gerade dafür schreibe ich doch hier, um für mein Vorhaben die richtige Architektur zu finden ...
Und wahrscheinlich steh ich auch aufm Schlauch :D

Also so wie ich es sehe gibt es zwei Arten der Unterscheidung:
- man übergibt das Objekt direkt mit
- man notified den Listener und dieser holt sich das neue Objekt selbst

Wenn man das Objekt übergibt, müsste das von dir gepostete Pattern mMn. wie folgt erweitern:
Pro Argument, das übergeben werden kann, brauch ich ein neues Observer Interface mit notify( AnyObject x) und zusätzlich noch je eine collection im Subject.
Dann kommt noch dazu, dass man ja nur die jeweilige Collection benachrichtigt haben will also würds auch mehrere notifyObservers() Methoden geben.

Soweit zu meinem verständnis :) ... wie gesagt: Evtl steh ich grad total aufm schlauch. Falls ja einfach erklären was ich falsch versteh :)

Hier mal ein Entwurf von mir ( der auch nicht optimal ist ) ... hier gibt es verschiedene Notifications Typen aber noch keine Objekt übergabe.
 

Anhänge

  • Unbenannt.PNG
    Unbenannt.PNG
    9,3 KB · Aufrufe: 152
`basTi schrieb:
Wenn man das Objekt übergibt, müsste das von dir gepostete Pattern mMn. wie folgt erweitern:
Pro Argument, das übergeben werden kann, brauch ich ein neues Observer Interface mit notify( AnyObject x) und zusätzlich noch je eine collection im Subject.
Dann kommt noch dazu, dass man ja nur die jeweilige Collection benachrichtigt haben will also würds auch mehrere notifyObservers() Methoden geben.

Mit der Standard-Implementierung von Java kannst du doch einfach das Objekt an den Observer übergeben, zusätzlich mit den Informationen, was geändert wurde etc. etc. in notifyObservers() kann man beliebig viele Objekte reinstecken und dann dementsprechend im Observer darauf reagieren, ohne zig einzel Interfaces konstruieren zu müssen.

Also z.B. auch notifyObservers(Object, NotificationType) und im Observer kann man dann feststellen wie viele Objekte mit reingesteckt wurden, welcher Art die sind und basierend darauf entscheiden, was passieren soll.
 
Zurück
Oben