sk.*_*sk. 9 oop callback inversion-of-control
当您使用生产者/消费者关系设计两个类时,如何避免循环依赖?这里ListenerImpl需要对Broadcaster的引用才能注册/取消注册,而Broadcaster需要一个引用回侦听器才能发送消息.此示例使用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); } }
}
Run Code Online (Sandbox Code Playgroud)
我不认为这是一个循环依赖.
听众取决于什么.
ListenerImpl依赖于Listener和Broadcaster
Broadcaster依赖于Listener.
Listener
^ ^
/ \
/ \
Broadcaster <-- ListenerImpl
Run Code Online (Sandbox Code Playgroud)
所有箭头都在Listener处结束.没有周期.所以,我觉得你没问题.
任何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)))
Run Code Online (Sandbox Code Playgroud)
(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))
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
不幸的是,Lisp消除了对它们的需求,解决了大多数设计模式问题(例如你的问题).
与赫姆斯的回答相反,我确实看到了一个循环。它不是依赖循环,而是引用循环:LI 保存 B 对象,B 对象保存 LI 对象(一个数组)。它们不容易自由,因此需要小心确保它们在可能的情况下自由。
一种解决方法是让 LI 对象持有对广播者的 WeakReference。理论上,如果广播公司已经消失,则无论如何都没有什么可以注销的,因此您的注销将简单地检查是否有要注销的广播公司,如果有则执行此操作。