Frage

Wie kann man zirkuläre Abhängigkeiten vermeiden, wenn Sie zwei Klassen mit einem Erzeuger / Verbraucher-Beziehung sind entwerfen? Hier muss ListenerImpl einen Verweis auf Broadcaster, um zu registrieren / unregister selbst und Broadcaster benötigt eine Referenz auf die Hörer, um Nachrichten zu senden. Dieses Beispiel ist in Java, aber es kann auf jede OO Sprache anwenden.

public interface Listener {
  void callBack(Object arg);
}
public class ListenerImpl implements Listener {
  public ListenerImpl(Broadcaster b) { b.register(this); }
  public void callBack(Object arg) { ... }
  public void shutDown() { b.unregister(this); }
}
public class Broadcaster {
  private final List listeners = new ArrayList();
  public void register(Listener lis) { listeners.add(lis); }
  public void unregister(Listener lis) {listeners.remove(lis); }
  public void broadcast(Object arg) { for (Listener lis : listeners) { lis.callBack(arg); } }
}
War es hilfreich?

Lösung

Ich sehe nicht, dass eine zirkuläre Abhängigkeit zu sein.

Listener hängt von nichts.

ListenerImpl hängt von Listener und Broadcaster

Broadcaster hängt von Listener.

        Listener
       ^        ^
      /          \
     /            \
Broadcaster <--  ListenerImpl

Alle Pfeile am Listener beenden. Es gibt keinen Zyklus. Also, ich glaube, Sie sind OK.

Andere Tipps

Jede OOP-Sprache? OKAY. Hier ist eine Zehn-Minuten-Version in CLOS.

Rundfunk Rahmen

(defclass broadcaster ()
  ((listeners :accessor listeners
              :initform '())))

(defgeneric add-listener (broadcaster listener)
  (:documentation "Add a listener (a function taking one argument)
  to a broadcast's list of interested parties"))

(defgeneric remove-listener (broadcaster listener)
  (:documentation "Reverse of add-listener"))

(defgeneric broadcast (broadcaster object)
  (:documentation "Broadcast an object to all registered listeners"))

(defmethod add-listener (broadcaster listener)
  (pushnew listener (listeners broadcaster)))

(defmethod remove-listener (broadcaster listener)
  (let ((listeners (listeners broadcaster)))
    (setf listeners (remove listener listeners))))

(defmethod broadcast (broadcaster object)
  (dolist (listener (listeners broadcaster))
    (funcall listener object)))

Beispiel Unterklassen

(defclass direct-broadcaster (broadcaster)
  ((latest-broadcast :accessor latest-broadcast)
   (latest-broadcast-p :initform nil))
  (:documentation "I broadcast the latest broadcasted object when a new listener is added"))

(defmethod add-listener :after ((broadcaster direct-broadcaster) listener)
  (when (slot-value broadcaster 'latest-broadcast-p)
    (funcall listener (latest-broadcast broadcaster))))

(defmethod broadcast :after ((broadcaster direct-broadcaster) object)
  (setf (slot-value broadcaster 'latest-broadcast-p) t)
  (setf (latest-broadcast broadcaster) object))

Beispielcode

Lisp> (let ((broadcaster (make-instance 'broadcaster)))
        (add-listener broadcaster 
                      #'(lambda (obj) (format t "I got myself a ~A object!~%" obj)))
        (add-listener broadcaster 
                      #'(lambda (obj) (format t "I has object: ~A~%" obj)))
        (broadcast broadcaster 'cheezburger))

I has object: CHEEZBURGER
I got myself a CHEEZBURGER object!

Lisp> (defparameter *direct-broadcaster* (make-instance 'direct-broadcaster))
      (add-listener *direct-broadcaster*
                  #'(lambda (obj) (format t "I got myself a ~A object!~%" obj)))
      (broadcast *direct-broadcaster* 'kitty)

I got myself a KITTY object!

Lisp> (add-listener *direct-broadcaster*
                    #'(lambda (obj) (format t "I has object: ~A~%" obj)))

I has object: KITTY

Leider löst Lisp die meisten der Design-Muster Probleme (wie Sie), indem die Notwendigkeit für sie zu beseitigen.

Im Gegensatz zu Herms' Antwort, I , um eine Schleife sehen. Es ist nicht eine Abhängigkeitsschleife, es ist eine eine Referenzschleife: das B LI Objekt enthält, das B-Objekt enthält (ein Array von) LI Objekt (e). Sie befreien nicht leicht, und Pflege muss getroffen werden, um sicherzustellen, dass sie frei, wenn möglich.

Eine Abhilfe ist einfach das LI-Objekt eine WeakReference an den Sender halten. Theoretisch, wenn der Sender weg ist, gibt es nichts mit ohnehin deregistrieren, so ist, dann wird einfach Ihre Deregistrierung überprüfen, ob ein Sender aus, abzumelden und tut so, wenn es.

Ich bin kein Java-Entwickler, aber so etwas wie folgt aus:

public class ListenerImpl implements Listener {
  public Foo() {}
  public void registerWithBroadcaster(Broadcaster b){ b.register(this); isRegistered = true;}
  public void callBack(Object arg) { if (!isRegistered) throw ... else ... }
  public void shutDown() { isRegistered = false; }
}

public class Broadcaster {
  private final List listeners = new ArrayList();
  public void register(Listener lis) { listeners.add(lis); }
  public void unregister(Listener lis) {listeners.remove(lis); }
  public void broadcast(Object arg) { for (Listener lis : listeners) { if (lis.isRegistered) lis.callBack(arg) else unregister(lis); } }
}

Verwenden schwache Verweise, den Zyklus zu brechen.

Siehe diese Antwort .

Hier ist ein Beispiel in Lua (ich meine eigenen benutzen Oop lib hier finden Sie Verweise auf " Object‘im Code).

Wie bei Mikael Jansson CLOS Beispiel Ihre Funktionen direkt nutzen können, wodurch die Notwendigkeit der Definition von Hörern zu entfernen (beachten Sie die Verwendung von ‚...‘, es ist Lua varargs):

Broadcaster = Object:subclass()

function Broadcaster:initialize()
    self._listeners = {}
end

function Broadcaster:register(listener)
    self._listeners[listener] = true
end

function Broadcaster:unregister(listener)
    self._listeners[listener] = nil
end
function Broadcaster:broadcast(...)
    for listener in pairs(self._listeners) do
        listener(...)
    end
end

Das Festhalten an der Implementierung, hier ist ein Beispiel, das in jeder dynamischen Sprache geschrieben werden könnte ich denke:

--# Listener
Listener = Object:subclass()
function Listener:callback(arg)
    self:subclassResponsibility()
end

--# ListenerImpl
function ListenerImpl:initialize(broadcaster)
    self._broadcaster = broadcaster
    broadcaster:register(this)
end
function ListenerImpl:callback(arg)
    --# ...
end
function ListenerImpl:shutdown()
    self._broadcaster:unregister(self)
end

--# Broadcaster
function Broadcaster:initialize()
    self._listeners = {}
end
function Broadcaster:register(listener)
    self._listeners[listener] = true
end
function Broadcaster:unregister(listener)
    self._listeners[listener] = nil
end
function Broadcaster:broadcast(arg)
    for listener in pairs(self._listeners) do
        listener:callback(arg)
    end
end
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top