2008-09-29 70 views
9

如何在设计两个生产者/消费者关系的类时避免循环依赖?这里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); } } 
} 

回答

8

我不认为这是一个循环依赖。

监听器依赖于什么。

ListenerImpl取决于听众和播音员

转播取决于监听器。

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

所有箭头结束于监听器。没有循环。所以,我认为你没问题。

+0

但还是有参考周期 - 播音员具有参考到具体的ListenerImpl对象,即使引用的类型是Listener接口类型。参考循环是否意味着依赖循环? – 2008-09-29 16:55:31

+1

没有真正的方法去除参考周期。这是这种类型的东西需要的。鉴于任何好的垃圾收集器都可以处理它们,所以引用周期现在不是真正的问题。它是你需要担心的依赖循环。 – Herms 2008-09-29 17:05:56

0

我不是一个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); } } 
} 
7

任何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中通过消除需要解决了大多数的设计图案的问题(如你的)为他们。

4

与Herms的回答相反,我看到一个循环。它不是一个依赖循环,它是一个参考循环:LI保存B对象,B对象保存(一个LI对象的数组)。他们不能轻松自由,需要注意确保他们尽可能自由。

一种解决方法是简单地让LI对象拥有对广播者的弱引用。从理论上讲,如果广播公司已经离开,那么无论如何都不需要注销,那么你的注销将只是检查是否有广播公司注销,如果有的话。

0

下面是Lua中的一个例子(我在这里使用我自己的Oop lib,参见代码中'Object'的引用)。

就像在Mikael Jansson的CLOS示例中,您可以直接使用函数,而不需要定义监听器(注意使用'...”,这是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 

坚持您的实现,下面是可以写在任何动态语言,我想一个例子:

--# 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 
相关问题