Generische Listener und Sender

Status
Nicht offen für weitere Antworten.

Rock Lobster

Bekanntes Mitglied
Servus,

bei meinem jetzigen Projekt hatte ich ursprünglich die Idee, einen Großteil der internen Kommunikation über ein Signalsystem zu realisieren. Es gibt eine GUI und einen Backend-Bereich, und sehr oft treten Ereignisse auf, die an mehreren Stellen von Bedeutung sind. Jede Klasse, die solche Benachrichtigungen braucht, hätte sich dann einfach für ein bestimmtes Signal bzw. für eine bestimmte Gruppe von Signalen registriert.

Mein Kollege meinte aber, ich soll bloß die Finger von Signalen lassen und lieber Listener verwenden, da das sauberer getrennt ist und so ein zentrales Signalsystem sehr schnell alles "zumüllt". Da mir das zunächst eingeleuchtet hat, habe ich dann auch für jeden Zweck einen eigenen Listener definiert, aber mittlerweile bin ich der Meinung, daß das viel mehr "zugemüllt" hat, als wenn ich Signale verwendet hätte (die Größe des Projekts ist meiner Meinung nach auch noch klein genug, daß Signale nicht großartig problematisch gewesen wären).

Ursprünglich wollte ich diese Listener irgendwie generisch anlegen, und das hat nicht funktioniert. Möglicherweise wäre es, wenn es denn doch ginge, weitaus übersichtlicher. Allerdings gibt's da eben ein Problem:

Hier mal ein typisches Beispiel, wie bei mir ein Listener/Sender-Paar aussieht:

Code:
public interface AudioPlayerListener
{
	public void eventOccured(PlayerMessage msg, AudioPlayerListenerSender sender);
	

	public class PlayerMessage
	{
		public enum Type
		{
			PLAY,
			STOP,
			PAUSE,
			
			REPOSITIONED,
			END_OF_MEDIA,
			
			TRACKS_ADDED,
			TRACKS_REMOVED,
			
			KILL,
		}
		
		public Type type;
		public AudioTrack track;
		
		public PlayerMessage(Type type)
		{
			this.type = type;
		}
		
		public PlayerMessage(Type type, AudioTrack track)
		{
			this.type = type;
			this.track = track;
		}
	}
	
	
	public abstract class AudioPlayerListenerSender
	{
		private HashSet<AudioPlayerListener> _audioPlayerListeners = new HashSet<AudioPlayerListener>();
		private PlayerMessage _message;
		
		public void addAudioPlayerListener(AudioPlayerListener l)
		{
			_audioPlayerListeners.add(l);
		}
		
		public void removeAudioPlayerListener(AudioPlayerListener l)
		{
			_audioPlayerListeners.remove(l);
		}
		
		protected void sendMessage(PlayerMessage msg)
		{
			for(AudioPlayerListener l : _audioPlayerListeners)
				l.eventOccured(msg, this);
		}
		
		protected void sendMessageWithoutBlocking(PlayerMessage msg)
		{
			_message = msg;
			
			new Thread()
			{
				@Override
				public void run()
				{
					sendMessage(_message);
				}
				
			}.start();
		}
	}
}

Mein Traum wäre es jetzt, diesen Code nur ein einziges mal zu benötigen und ihn über <generische Parameter> an die jeweilige Message anzupassen. Das hieße also, ich würde "PlayerMessage" von einem allgemeinen "Message"-Typ ableiten. Nun funktioniert das auch für den Listener, den ich als public interface AudioPlayerListener<T extends Message> deklarieren würde. Allerdings hakt es dann beim Sender, denn der Sender besitzt ja eine Liste seiner Listener, und diese wird zwar ebenfalls parametrisiert, allerdings hat sie einen eindeutigen Namen - was letztendlich verhindern würde, daß man eine Klasse von mehreren Sendern ableiten könnte.

Die einzige Lösung hierfür wäre, einen einzigen Sender zu definieren, der dann eben auch sämtliche Arten von Messages durchschicken könnte. Allerdings ist das dann wieder sehr großzügig, weil der Sender nun "allmächtig" ist, und die Ordentlichkeit wäre wiederum zerstört. Außerdem müßte dieser eine einzige Liste mit allen möglichen Listenern halten, und bei jeder Benachrichtigung eines Listeners zunächst dessen Message-Typ überprüfen.

Ich habe schon die verschiedensten Konstellationen durchprobiert, aber es hängt dann letztendlich immer an irgendeiner Stelle. Hat jemand von euch schonmal erfolgreich solch ein Listener/Sender-Paar hingekriegt, das vollkommen generisch arbeiten kann und "sauber" ist? Oder sollte man doch lieber Signale verwenden? ;)
 
S

SlaterB

Gast
> denn der Sender besitzt ja eine Liste seiner Listener, und diese wird zwar ebenfalls parametrisiert, allerdings hat sie einen eindeutigen Namen

gehts um den Namen der Variablen (einfach allgemein 'listenerList' lassen)
oder um den Typ der Elemente? dieser Typ kann in der Basisklasse natürlich noch nicht festgelegt sein, muss ein Typparameter der Klasse sein
 

Rock Lobster

Bekanntes Mitglied
Nein natürlich geht's um den Namen. Denn wenn der "listenerList" heißt oder sowas, dann kann ich ja eine Klasse nicht mehrfach ableiten von verschiedenen Sendern, weil sich dann eine Liste über die andere legen würde. Somit ist es nicht möglich, eine Klasse zu haben, die mehrere Sender in sich vereint. Aber da fällt mir grad ein, die Sender könnten einfach vollwertige Klassen sein, also nicht abstrakt, und die betreffende Klasse könnte einfach als Member diese Sender haben. Wäre aber auch nicht so "schön".

Und wenn's halt einfach nur 'ne "listenerList" nur gibt, dann muß es wiederum eine sein, die nicht parametrisiert ist, damit dieser Sender möglichst an alle möglichen Listener schicken kann. Aber dann kommt wieder das "ist ja unsauber"-Problem...
 
S

SlaterB

Gast
> Somit ist es nicht möglich, eine Klasse zu haben, die mehrere Sender in sich vereint.

das ist so oder so nicht möglich, du kannst ja nicht von mehreren Klassen erben oder wie stellst du dir das vor?

immer nur paar halbe Sätze und man soll sich den verrückten Rest dazudenken?
Beispiele!
 

Rock Lobster

Bekanntes Mitglied
Naja also halbe Sätze


;)

Naja also halbe Sätze finde ich keine.
Von mehreren Klassen erben ist so oder so nicht möglich, das ist korrekt, aber man könnte immerhin über mehrere hierarchische Stufen davon erben, was in besonderen Fällen auch sinnvoll sein kann.

Das Problem insgesamt ist auch, daß ich diese Problematik vor mehreren Wochen bereits hatte und mich nicht mehr an alle einzelnen Fälle erinnern kann. Fakt ist aber, daß ich mehrere unterschiedliche Lösungen versucht habe, und es gab immer an irgendeiner Stelle eine Art "Schranke", wo irgendwas nicht mehr möglich war.
-> Daher frage ich nun einfach mal andersrum: Hat jemand von euch schonmal ein sauberes Listener/Sender-System programmiert, ohne den gleichen Code immer und immer wieder kopieren zu müssen? Oder gibt es hierfür keine saubere Lösung?
 
S

SlaterB

Gast
> aber man könnte immerhin über mehrere hierarchische Stufen davon erben, was in besonderen Fällen auch sinnvoll sein kann.

und was hat das dann mit dem Namen einer einzelnen Variable zu tun?
nur weil du mehrfach erbst wird sich eine Variable doch nicht per Zauberhand verdoppeln

immernoch: nur vage Gedanken ohne dass klar ist, worum es überhaupt geht :bae:

> ohne den gleichen Code immer und immer wieder kopieren zu müssen?

ganz einfaches Spiel für jede Art von Anwendung auf dieser Welt:
gemeinsamer Code gehört in eine Basisklasse

wenn man aber mehrere unterschiedliche Mengen an gemeinsamen Code hat, oder den einen Code für verschiedene Zwecke mehrmals haben möchte,
dann Membervariablen verwenden wie du ja auch schon erkannt hast,

entweder mit nur einem simplen getter auf den Member, oder mit delegate-Operationen, z.B.

private List liste;

public int size() {
return liste.size();
}
 

Wildcard

Top Contributor
Dein Vorhaben ist IMO Unsinn. Wie viele verschiedene Listener brauchst du denn wirklich und bei wie vielen lässt sich das über ein passendes Event Objekt realisieren?
Und ja, der Code für die Listener Behandlung ist in aller Regel sehr ähnlich, aber aus eigener Erfahrung kann ich nur sagen das sich die Anzahl der Klassen die Event-Handling erlauben in den meisten Projekten im Rahmen hält.
Auch sowas ist mehr als fragwürdig:
Code:
protected void sendMessageWithoutBlocking(PlayerMessage msg)
Der Listener muss selbst dafür sorgen das er seine Aufgabe zeitnah erledigt. Wo das nicht möglich ist startet er einen Thread. Durch eine solche Methode ist sich der Listener aber nie bewußt in welchem Kontext er läuft, was zu großen Problemen führen kann.
Schau dir doch einfach mal die Listener in Swing an. That's the way to go.
 

Rock Lobster

Bekanntes Mitglied
Das mit den vagen Gedanken liegt einfach daran, daß es schon 'ne Weile her ist, und ich noch genau weiß, wie ich zig Geistesblitze hatte, die sich hinterher alle als fehlerhaft erwiesen, hab grad noch 'nen kleinen Test probiert, der wieder an einer Stelle hängt ;)

Naja das mit den Member-Variablen werd ich mal noch überdenken. Möglicherweise ist für meine Zwecke doch ein Signalsystem das beste - die einzelnen Komponenten werden nämlich außerhalb nicht wiederverwendet, sie sind intern nur fest verdrahtet. Aber da sich das immer wieder erweitern kann, wäre es halt schick, wenn sich jede neue Klasse einfach kurz ins Signalsystem "einklinken" könnte.

Hier übrigens mal, falls es Dich interessiert, mein jetziger Versuch (die Messages wurden zum Testen etwas vereinfacht):
Code:
	public interface Message	{}
	
	
	public enum YeahMessage implements Message
	{
		YEAH_MESSAGE_ONE,
		YEAH_MESSAGE_TWO,
		YEAH_MESSAGE_THREE,
	}
	
	
	public enum RockMessage implements Message
	{
		ROCK_MESSAGE_ONE,
		ROCK_MESSAGE_TWO,
	}
	
	
	public interface GenericListener<T extends Message>
	{
		public void eventOccured(T msg);
	}
	
	
	public interface YeahListener extends GenericListener<YeahMessage>	{}
	public interface RockListener extends GenericListener<RockMessage>	{}
	
	
	public abstract class GenericSender
	{
		private List<GenericListener<?>> listeners;
		
		public void sendMessage(Message msg)
		{
			for(GenericListener<?> listener : listeners)
			{
				if (listener.getClass().getTypeParameters()[0].getClass() == msg.getClass())
					listener.eventOccured(msg);
			}
		}
		
		public void addListener(GenericListener<?> l)
		{
			listeners.add(l);
		}
	}

Der Fehler liegt in Zeile 39, hier kann er die Message nicht durchschicken, weil er nicht weiß, mit welchem Datentyp er es zu tun hat, und offenbar weiß er auch nicht, daß diese immer von Message abgeleitet sind...
 

Rock Lobster

Bekanntes Mitglied
@ Wildcard: Es ist aber mittlerweile schon sehr durchwachsen, auch wenn ich erst dachte, daß es niemals so kompliziert werden kann. Es ist ein bißchen schwer zu beschreiben, wenn man das Projekt nicht kennt.

Das schlimmste ist eigentlich auch, daß man die Listener immer irgendwie anmelden muß, und das entsprechende Objekt ist dann meist irgendwo außer Reichweite und muß über Umwege irgendwie geholt werden. Beispielsweise gibt es einen Player, und es gibt ein PlayerPanel. Dieses Panel weiß vom Player, hat selbst aber noch ein paar weitere Unterpanels, die zum Teil wiederum noch ein paar Objekte beinhalten, die gerne vom Player Messages empfangen würden. Wenn diese sich nun am Player anmelden wollen, muß das immer umständlich über zig Klassen hinweg "durchgereicht" werden. Auch dafür wäre es schön, wenn es eine schicke Lösung gäbe, aber mir ist außer Signalen bisher noch nix eingefallen.

Das mit dem Thread beim Senden war bei mir teilweise notwendig, mittlerweile weiß ich aber auch nicht mehr genau, wofür - das Projekt besteht eben schon seit 'ner Weile, und ich war in den letzten Tagen selten in der Firma. Lag wohl dran, daß die Listener in ihren Behandlungsroutinen dann eben irgendwelche zeitaufwendigen Dinge vollbracht haben, die somit den Sender für gewisse Zeit lahm gelegt hätten.

EDIT: Ich weiß jetzt auch wieder warum. Es gibt eine Klasse "AudioTrack", diese hat einen Thread der den Track abspielt. Innerhalb dieses Threads kann eine Message an den Player erfolgen, und der Player wiederum stoppt dann den Track, welcher wiederum den Thread stoppt. Wenn nun diese Message-Anweisung jedoch nicht in einem Thread erfolgt, kehrt die Funktion niemals zurück, und der gesamte Player hängt sich auf. Dieses unangenehme Problem hatte ich schon mehrmals. Aber Du hast recht, den Thread hätte ich auch direkt im Listener aufmachen können.
 
S

SlaterB

Gast
mit Generics kann man für sich noch ne Menge Ärger haben..

eine Standard-Variante wäre
Code:
public abstract class GenericSender<T extends Message>
{
    private List<GenericListener<T>> listeners;

    public void sendMessage(T msg)
    {
        for (GenericListener<T> listener : listeners)
        {
            listener.eventOccured(msg);
        }
    }

    public void addListener(GenericListener<T> l)
    {
        listeners.add(l);
    }
}
 

Wildcard

Top Contributor
@Rock Lobster
Alles was du beschreibst hört sich nach einem 'gewachsenen' Projekt an, das nie richtig geplant wurde.
Messages führen bei grafischen Anwendungen meiner Meinung zur Katastrophe. Ich durfte selbst bereits eine sehr große Message basierte Anwendung auf Listener umbauen, ein Spaß war das nicht unbedingt.
Setz dich mal in Ruhe hin und überleg dir ob dein Design wirklich passt.
Auch ein Buch über Design Patterns wird dein Schaden nicht sein.
 

Rock Lobster

Bekanntes Mitglied
Jepp, das wäre auch meine Version gewesen, mit dem Nachteil halt, daß ein Sender nur noch eine einzige Message-Art verschicken kann. Aber ich glaube, im Moment passiert dies in meinem Projekt auch nicht anders - ich wollte mir glaub nur die Möglichkeiten offen halten.

Was meint ihr denn generell zum Thema Listener und Signale? Würdet ihr Signale bevorzugen in einem in sich abgeschlossenen System, dessen Klassen eigentlich nie auch in völlig anderem Kontext verwendet werden, und wo die Logik mehr oder weniger statisch verdrahtet ist, aber eben erweiterbar bleiben soll? Oder würdet ihr ebenfalls grundsätzlich Listener benutzen? Wie gesagt, die Anmeldung am Listener ist auch so ein Ding, welches immer äußerst unkomfortabel ist.
 

Rock Lobster

Bekanntes Mitglied
Jepp, es ist durchaus ein gewachsenes Projekt, allerdings wurde nur der Backend-Bereich richtig geplant, das Frontend wollte ich einfach nur noch "draufsetzen", mußte aber feststellen, daß dies komplexer ist, als man sich es denken kann.
 
Status
Nicht offen für weitere Antworten.
Ähnliche Java Themen
  Titel Forum Antworten Datum
M generische Listener Allgemeine Java-Themen 2
Zrebna Random Number - Generische Formel zwischen zwei INKLUSIVEN Werten Allgemeine Java-Themen 16
F Verständnisprobleme Aufgabenstellung Aktionsobjekte und generische Listen Allgemeine Java-Themen 1
J Generische Interface - Problem Allgemeine Java-Themen 3
J Generische Interfaces mehrfach einbinden Allgemeine Java-Themen 11
M Methoden Generische Methode für ArrayList Allgemeine Java-Themen 7
perlenfischer1984 Reflection : Element in generische Liste hinzufügen Allgemeine Java-Themen 4
D generische Interface und konkrete Methode Allgemeine Java-Themen 3
T Interface mit generische Typen Allgemeine Java-Themen 5
A Methoden Generische Methode mit Arrays - Source Compatibility 1.7 benötigt, wieso? Allgemeine Java-Themen 3
M Interface Generische Klassen mit mehreren Typen überschreiben Allgemeine Java-Themen 0
H Interface Generische Schnittstelle (rekusiv) Allgemeine Java-Themen 2
C generische Authentifizierung Allgemeine Java-Themen 7
JCODA Generische Map Frage Allgemeine Java-Themen 10
H Generische Array Allgemeine Java-Themen 11
M Problem beim schreiben einer eigene generische Klasse Hashtable Allgemeine Java-Themen 11
M Generische Methoden mit Java und globale Variablen Allgemeine Java-Themen 9
M Problem beim schreiben einer eigene generische Klasse LinkedList Allgemeine Java-Themen 34
D generische Klasse für alle Maps (nicht Collections :-)) Allgemeine Java-Themen 11
D Methode für generische enummap/enum Allgemeine Java-Themen 10
B Generische Datentypen MergeSort Allgemeine Java-Themen 5
M Generische Klassen Allgemeine Java-Themen 3
S Generische Typen: Frage dazu Allgemeine Java-Themen 11
H generische Klasse Realtion Allgemeine Java-Themen 2
T Ideenfindung: Generische Transportklasse? Allgemeine Java-Themen 3
C Generische Methode (Schablone) Allgemeine Java-Themen 8
G generische Klasse als Parameter einer generischen Klasse Allgemeine Java-Themen 5
leifg Rechenoperationen auf generische Datentypen Allgemeine Java-Themen 10
B Generische Typen instanzieren Allgemeine Java-Themen 11
M Generische Datentypen Allgemeine Java-Themen 14
S Generische Liste Allgemeine Java-Themen 30
F Viele generische Parameter sinnvoll? oder besser casten? Allgemeine Java-Themen 10
S Generische Methode Allgemeine Java-Themen 13
R Frage zu einfügen in generische lineare Liste Allgemeine Java-Themen 7
S Generische Methoden Allgemeine Java-Themen 7
D Statische, generische Methode will nicht. Allgemeine Java-Themen 2
berserkerdq2 Was heißt es mit FXML Listener zu setzen ind Buttons zu erstellen? Allgemeine Java-Themen 6
L Wie programmiert man einen Listener? Allgemeine Java-Themen 1
S Threads Serielle Schnittstelle mit Listener / Aufrufendes GUI Allgemeine Java-Themen 4
H Listener Verständnisproblem Allgemeine Java-Themen 5
D Action Listener Problem Allgemeine Java-Themen 3
S Key Listener mit Strg + s Allgemeine Java-Themen 12
K Threads Listener für Future finish Allgemeine Java-Themen 2
TheWhiteShadow Listener Reihenfolge Allgemeine Java-Themen 4
D Listener auf System.out.err / System.out.println Allgemeine Java-Themen 5
A Listener für constructor einer bestimmen Klasse Allgemeine Java-Themen 9
E Bild mit Listener einfügen Allgemeine Java-Themen 3
P Listener für Tastatureingaben ohne Focus Allgemeine Java-Themen 4
serjoscha WatchDog Thread und Listener Allgemeine Java-Themen 10
hdi Listener für Desktop-Veränderungen Allgemeine Java-Themen 7
T JTextField in Listener leeren Allgemeine Java-Themen 2
lenniii Listener & JList Allgemeine Java-Themen 3
K Key listener ohne Fokus aktiv behalten ? Allgemeine Java-Themen 12
G Code-Convention für Listener Allgemeine Java-Themen 2
T Observer vs Listener Allgemeine Java-Themen 18
S Listener Allgemeine Java-Themen 3
P Abstrakte Elternklasse als Listener Allgemeine Java-Themen 6
MQue KeyAction listener Allgemeine Java-Themen 2
S Listener für Kalender Allgemeine Java-Themen 2
B Observer vs Listener (GUI-Programmierung) Allgemeine Java-Themen 5
J Listener für Ende eines Threads gesucht... Allgemeine Java-Themen 5
S Listener Implentierung bei Eingabe in Textfeld Allgemeine Java-Themen 2
G Objekt einer inneren Klasse als Listener Allgemeine Java-Themen 3
D Boolean Listener? Allgemeine Java-Themen 28
M Listener und Performance Allgemeine Java-Themen 9
D Observer/Observable Pattern vs. Listener-Konzept Allgemeine Java-Themen 4
G Listener auf aktuelles Datum / Uhrzeit Allgemeine Java-Themen 2
Escorter Eigenen Listener schreiben Allgemeine Java-Themen 3
E Gibt es so etwas wie einen Windows Listener? Allgemeine Java-Themen 6
T SelectionProvider und Listener Frage Allgemeine Java-Themen 14
I Listener, der zeitlich-periodisch aufgerufen wird Allgemeine Java-Themen 3
B Listener dynamisch setzen Allgemeine Java-Themen 6
A Listener Tastatur Allgemeine Java-Themen 3
E Wie: Eigener Listener, eigenes Event (möglichst einfach) Allgemeine Java-Themen 29
D Thread vs Listener Allgemeine Java-Themen 6
K Listener einem Objekt hinzufügen Allgemeine Java-Themen 3
S Listener-Problematik in komplexem Projekt Allgemeine Java-Themen 2
I Listener/Event selber machen. Allgemeine Java-Themen 4
M Time Listener ? Allgemeine Java-Themen 2
Z Zwei Listener zwischen zwei Klassen Allgemeine Java-Themen 4
B statusbar update -> mit thread oder listener? Allgemeine Java-Themen 7
M Action-listener Allgemeine Java-Themen 6
M Bean Listener hinzufügen und Methode später überschreiben Allgemeine Java-Themen 27
G Flavor-Listener Allgemeine Java-Themen 6
E Listener für Änderung der Fenstergröße Allgemeine Java-Themen 3
W Listener an ein beliebiges Objekt hänger, der mir Änderung Allgemeine Java-Themen 8
Z Problem beim Aufruf eines Listener? Allgemeine Java-Themen 4
A HILFE: subclass von Hashtable mit listener aufstellen Allgemeine Java-Themen 5
G Key-Event-Sender Allgemeine Java-Themen 5

Ähnliche Java Themen

Neue Themen


Oben