使用回调时如何避免循环依赖?
时间:2020-03-06 14:52:58 来源:igfitidea点击:
当设计两个具有生产者/消费者关系的类时,如何避免循环依赖?在这里,ListenerImpl需要对Broadcaster的引用才能注册/注销自己,而Broadcaster需要对Listeners的引用才能发送消息。这个例子是用Java编写的,但是它可以应用于任何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); } } }
解决方案
我不认为这是循环依赖。
侦听器不依赖任何内容。
ListenerImpl取决于侦听器和广播器
广播者取决于侦听器。
Listener ^ ^ / \ / \ Broadcaster <-- ListenerImpl
所有箭头都在侦听器处结束。没有周期。所以,我认为你还好。
我不是Java开发人员,而是这样的:
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); } } }
任何OOP语言?好的。这是CLOS的十分钟版本。
(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)))
(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))
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
不幸的是,Lisp通过消除对大多数设计模式问题(例如问题)的解决。
与Herms的回答相反,我确实看到了一个循环。这不是依赖关系循环,而是参考循环:LI保存B对象,B对象保存LI对象(的一个数组)。它们不容易释放,因此需要注意确保它们在可能的情况下释放。
一种解决方法是,仅使LI对象持有对广播公司的WeakReference。从理论上讲,如果广播公司已经离开,则没有任何要注销的内容,因此注销将仅检查是否有广播公司要注销,如果有,则注销。
使用弱引用来打破循环。
看到这个答案。
这是Lua中的示例(我在这里使用我自己的Oop库,请参见代码中对"对象"的引用)。
就像在Mikael Jansson的CLOS示例中一样,我们可以直接使用函数,而无需定义侦听器(请注意使用" ...",这是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
坚持实现,这是一个可以用任何动态语言编写的示例:
--# 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