Pergunta

Como você pode evitar dependências circulares quando você está projetando duas classes com um relacionamento produtor / consumidor? Aqui ListenerImpl precisa de uma referência a Broadcaster, a fim de registrar / cancelar o registro próprio, e Broadcaster precisa de uma volta de referência para os ouvintes, a fim de mensagens de envio. Este exemplo é em Java, mas pode aplicar-se a qualquer linguagem OO.

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); } }
}
Foi útil?

Solução

Eu não ver que ser uma dependência circular.

Listener não depende de nada.

ListenerImpl depende Listener e Broadcaster

Broadcaster depende Listener.

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

Todas as setas terminar em Listener. Não há nenhum ciclo. Então, eu acho que você está OK.

Outras dicas

Qualquer linguagem OOP? ESTÁ BEM. Aqui está uma versão de dez minutos em CLOS.

quadro Broadcasting

(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)))

Exemplo subclasse

(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))

Exemplo de código

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

Infelizmente, resolve Lisp maioria dos problemas padrão de design (como a sua), eliminando a necessidade para eles.

Em contraste com a resposta Herms', eu do ver um loop. Não é um loop de dependência, é um um lacete de referência: LI contém o objecto B, o objecto B contém (uma matriz de) objeto LI (s). Eles não livre com facilidade, e necessidades de cuidados a tomar para garantir que eles libertar quando possível.

Uma solução é simplesmente ter o objeto LI realizar uma WeakReference para a emissora. Teoricamente, se a emissora foi embora, não há nada para cancelar com qualquer maneira, então, em seguida, o seu cancelamento simplesmente verificar se há uma emissora de cancelar o registro de, e fazê-lo se houver.

Eu não sou um dev java, mas algo como isto:

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); } }
}

Utilize referências fracas para quebrar o ciclo.

Consulte esta resposta .

Aqui está um exemplo em Lua (eu uso meu próprio Oop lib aqui, ver as referências a " objeto' no código).

Tal como no exemplo CLOS de Mikael Jansson, suas funções de uso pode diretamente, eliminando a necessidade de definir ouvintes (note o uso de '...', é varargs da Lua):

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

Furar a sua implementação, aqui está um exemplo que poderia ser escrito em qualquer linguagem dinâmica Acho:

--# 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
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top