mercancia mejorar las evitar devoluciones como oop inversion-of-control callback

oop - mejorar - ¿Cómo pueden evitarse las dependencias circulares cuando se utilizan devoluciones de llamada?



como evitar devoluciones de mercancia (6)

Cualquier lenguaje OOP? DE ACUERDO. Aquí hay una versión de diez minutos en CLOS.

Marco de radiodifusión

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

Ejemplo de subclase

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

Código de ejemplo

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

Desafortunadamente, Lisp resuelve la mayoría de los problemas de patrones de diseño (como el tuyo) al eliminar la necesidad de ellos.

¿Cómo se pueden evitar las dependencias circulares cuando se diseñan dos clases con una relación productor / consumidor? Aquí ListenerImpl necesita una referencia a Broadcaster para registrarse / anular el registro, y Broadcaster necesita una referencia de vuelta a los Listeners para enviar mensajes. Este ejemplo está en Java, pero puede aplicarse a cualquier lenguaje 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); } } }


En contraste con la respuesta de Herms, veo un ciclo. No es un bucle de dependencia, es un bucle de referencia: LI tiene el objeto B, el objeto B tiene (una matriz de) objetos LI. No se liberan fácilmente, y se debe tener cuidado para asegurarse de que sean gratuitos cuando sea posible.

Una solución alternativa es simplemente hacer que el objeto LI tenga una WeakReference para la emisora. Teóricamente, si la emisora ​​se ha ido, no hay nada que anular el registro de todos modos, por lo que su cancelación de registro simplemente comprobará si hay una emisora ​​para anular el registro, y lo hará si existe.


No soy un desarrollador de Java, pero algo como esto:

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


No veo que sea una dependencia circular.

El oyente no depende de nada.

ListenerImpl depende de Listener y Broadcaster

El locutor depende del oyente.

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

Todas las flechas terminan en Listener. No hay ciclo Entonces, creo que estás bien.


Aquí hay un ejemplo en Lua (utilizo mi propia libreria Oop aquí, veo referencias a ''Objeto'' en el código).

Al igual que en el ejemplo de CLOS de Mikael Jansson, puede usar funciones directamente, eliminando la necesidad de definir oyentes (tenga en cuenta el uso de ''...'', son los varargs de 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

Siguiendo con su implementación, aquí hay un ejemplo que podría escribirse en cualquier lenguaje dinámico, supongo:

--# 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