Java-Listener-Vererbung

7

Ich habe eine Java-Klasse, die benutzerdefinierte Java-Ereignisse auslöst. Die Struktur des Codes ist wie folgt:

%Vor%

Alles funktioniert korrekt, aber ich möchte eine neue Unterklasse von A erstellen (nennen Sie es B), die ein neues Ereignis auslösen kann. Ich denke an die folgende Änderung:

%Vor%

Ist das der richtige Ansatz? Ich habe im Internet nach Beispielen gesucht, konnte aber keine finden.

Es gibt ein paar Dinge, die ich in dieser Lösung nicht mag:

  1. BListener hat zwei Methoden, die AEvent verwenden, die andere BEvent als Parameter.
  2. B class hat beide Methoden addAListener und addBListener . Soll ich addAListener mit einem privaten Schlüsselwort ausblenden? [UPDATE: Es ist nicht möglich, mit einem privaten Schlüsselwort zu verbergen]
  3. Ähnliches Problem mit fireAListenerEvent1 und fireBListenerEvent1 Methoden.

Ich benutze Java Version 1.5.

    
asalamon74 16.12.2008, 08:17
quelle

6 Antworten

12

Ich sehe keinen Grund, warum BListener AListener erweitern sollte.

Möchten Sie wirklich alle an B -Ereignissen Interessierten dazu bringen, auch event1() zu implementieren?

Sie können auch addAListener() nicht hinzufügen, da eine abgeleitete Klasse die Sichtbarkeit einer Methode, die in der Elternklasse vorhanden ist, nicht verringern kann. Außerdem sollten Sie das Liskov-Substitutionsprinzip nicht verletzen oder verletzen (jedes B muss alles können) ein A kann tun).

Und als letzte Bemerkung würde ich die fire*() Methoden schützen. Es gibt normalerweise keinen Grund, sie öffentlich zu machen, und die Anzahl der öffentlichen Mitglieder zu reduzieren, hält Ihre öffentliche Schnittstelle sauber.

    
Joachim Sauer 16.12.2008, 08:30
quelle
6

Verwende keine Erbschaft, es ist nicht das, was du willst und es wird zu einem spröden und schwer zu ändernden Design führen. Die Komposition ist ein flexiblerer und besserer Ansatz für das Design. Versuchen Sie immer, Interfaces so granular wie möglich zu gestalten, da sie nicht geändert werden sollten. Sie sind Ihr Vertrag mit dem Rest des Systems. Wenn neue Funktionen hinzugefügt werden müssen, besteht die erste Möglichkeit darin, dem Ereignis weitere Informationen hinzuzufügen. Wenn das nicht angemessen ist, sollten Sie eine neue Schnittstelle für die Übermittlung dieses Ereignisses entwerfen. Dies verhindert, dass existierender Code geändert werden muss, der nicht betroffen ist.

Hier ist mein Lieblingsmuster dafür, ich glaube, es wird allgemein als Beobachter bezeichnet.

Erstellen Sie eine neue Schnittstelle, die Methoden für diesen Ereignistyp definiert (fooEvent () addFooEventListener () removeFooEventListener ()). Implementieren Sie diese Schnittstelle in der konkreten Klasse, die diese Ereignisse generiert. (Ich nenne das normalerweise so etwas wie SourcesFooEvent, FiresFooEvent, FooEventSource, usw.)

Wenn Sie die Code-Duplizierung reduzieren möchten, können Sie eine Hilfsklasse erstellen, die die Registrierung der Listener behandelt, sie in einer Sammlung speichert und eine Fire-Methode für die Veröffentlichung der Ereignisse bereitstellt.

Generika können hier helfen. Zuerst eine generische Listener-Schnittstelle:

%Vor%

Als nächstes eine passende EventSource-Schnittstelle:

%Vor%

Endlich eine abstrakte Basisklasse, um schnell eine Hilfsklasse für die Registrierung von Listenern und die Ereignisverteilung zu erstellen:

%Vor%

Sie würden den abstrakten EventDispatcher durch Kapselung verwenden, so dass jede andere Klasse EventSource einfach implementieren kann, ohne dass sie eine bestimmte Klasse erweitern muss.

%Vor%

Dies zeigt hoffentlich das gute Gleichgewicht zwischen Typensicherheit (wichtig), Flexibilität und Wiederverwendung von Code.

    
Mark Renouf 29.01.2009 15:54
quelle
3

Ich verstehe von deinem Kommentar zu saga, dass das feuern von B automatisch A feuern wird.

Warum nicht einen einzelnen Typ von Listener verwenden und dann einige Vererbung, Delegation und Generika mischen?

%Vor%

Erläuterung: Der Code zum Verwalten von Listenern ist im Listener-Manager der Basisklasse freigegeben. B kann nur aufgrund der generischen Kompilierzeitprüfung BListener empfangen. Feuern B wird automatisch A auslösen.

    
Yoni Roit 16.12.2008 13:42
quelle
3

Es scheint mir, dass Sie die Dinge sehr einfach halten können.

Mein understading

  • Sie haben eine Basisklasse A , die einige basicOperation

  • ausführt
  • Sie haben möglicherweise eine spezifischere Unterklasse B , die außerdem einige specificOperations

  • ausführen kann

Wenn das der Fall ist, müssen Sie beide Ereignisse behandeln ( basic für A und basic + spezifisch für B)

Nun, Sie müssen die Methoden nicht überladen, um dies zu tun. Sie müssen nur bestimmte Handler (oder Listener) für bestimmte Ereignisse hinzufügen.

Es kann der Fall sein, dass das Ereignis "grundlegend" ist, das ist in Ordnung.

Aber wenn das Ereignis spezifisch ist, müssen Sie entsprechend reagieren. Also, was ich tun würde, ist ein Check in dem spezifischen Listener hinzuzufügen, um das spezifische Ereignis wie folgt zu unterscheiden:

%Vor%

Und das ist es.

Ihre Beschreibung des Problems ist zu abstrakt, daher können keine konkreten Lösungen vorgeschlagen werden. Aber wenn es schwierig ist zu erklären, was Sie erreichen möchten, müssten Sie wahrscheinlich zuerst analysieren, was das Problem an erster Stelle ist.

Wenn mein oben stehendes Verständnis korrekt ist (Sie müssen basic + spezifisch einige Male handhaben), kann der folgende lange Code hilfreich sein.

Mit freundlichen Grüßen

%Vor%

Beispielausgabe:

%Vor%

Wenn Sie weiter gehen, können Sie sogar die Schnittstellen loswerden, um es einfacher zu halten

    
OscarRyz 29.01.2009 03:50
quelle
1

Wenn

%Vor%

können Sie nicht etwas wie:

%Vor%

Wie ich im Kommentar gesagt habe, bin ich mir nicht sicher, was Sie eigentlich erreichen wollen? Wer wird von wo aus angerufen, und was muss er tun, wenn er angerufen wird?

    
Slartibartfast 27.01.2009 14:43
quelle
1

Basierend auf den wenigen Informationen, die wir über die Beziehung zwischen A & amp; B , ich denke, es ist verwirrend, BListener zu einer Subschnittstelle von AListener zu machen. Wie der Name schon sagt, sollte BListener auf BEvent s warten, die bereits eine Unterklasse von AEvent s sind. Aus Gründen der Klarheit sollten Zuhörer anspruchsvolle Zwecke haben; Sie sollten sich nicht unnötig überlappen. Außerdem sind solche überlappenden Listener nicht notwendig, da Sie bereits separate Methoden in der Klasse B definieren, um verschiedene Arten von Listenern zu behandeln.

Um diesen Punkt zu verdeutlichen, betrachten Sie dieses Beispiel nach Ihrem Code:

%Vor%

Dieses Design funktioniert, ist aber verwirrend, weil ClickableMouseListener zwei Arten von Ereignissen behandelt und ClickableMouseWidget zwei Arten von Listenern behandelt, wie Sie richtig angemerkt haben. Betrachten Sie nun die folgende Alternative, die Komposition statt Vererbung verwendet:

%Vor%     
Zach Scrivena 29.01.2009 15:19
quelle

Tags und Links