Java 如何使用观察者模式避免循环引用?
我遇到了这样的情况:Java 如何使用观察者模式避免循环引用?,java,oop,observer-pattern,circular-reference,Java,Oop,Observer Pattern,Circular Reference,我遇到了这样的情况: public class A { private ArrayList<Listener> listeners = new ArrayList<Listener>(); public A() { // stub } public void addListener(Listener listener) { this.listeners.add(listener);
public class A
{
private ArrayList<Listener> listeners = new ArrayList<Listener>();
public A()
{
// stub
}
public void addListener(Listener listener)
{
this.listeners.add(listener);
}
public void removeListener(Listener listener)
{
this.listeners.remove(listener);
}
public interface Listener
{
// stub
}
}
public class B implements A.Listener
{
private A instanceOfA;
public B()
{
this.instanceOfA = new A();
this.instanceOfA.addListener(this);
}
}
public class MessageListenerImpl implements MessageListener
{
/** The driver. */
private DeviceClassInternal driver;
/** The log. */
private Log log;
/**
* Instantiates a new TCP message listener.
*
* @param driver the driver
* @param log the log
*/
public MessageListenerImpl( DeviceClassInternal driver, Log log )
{
this.driver = driver;
this.log = log;
}
/** {@inheritDoc} */
@Override
public void MessageReceived( byte[] data ) throws IOException
{
log.info( "data received: {1}", new String( data ) );
driver.dataReceived( data );
}
@Override
public void closed()
{
log.info( "{0}: Connection closed", physicalConnection.getName() );
driver.closed();
}
}
公共A类
{
私有ArrayList侦听器=新ArrayList();
公共A()
{
//存根
}
公共void addListener(侦听器侦听器)
{
this.listeners.add(listener);
}
公共void RemovelListener(侦听器侦听器)
{
this.listeners.remove(listener);
}
公共接口侦听器
{
//存根
}
}
公共类B实现了一个侦听器
{
私人A instanceOfA;
公共图书馆B()
{
this.instanceOfA=新的A();
this.instanceOfA.addListener(this);
}
}
我相信B永远不会被销毁,因为A保留了对它的引用,而A永远不会被销毁,因为B保留了对它的引用
这似乎是一种常见的设计模式,但没有人解释如何避免这种循环引用?,因为您没有按照模式通常定义的方式进行操作。你可以看看这个 将侦听器定义为如下所示的接口,例如:
/**
* The listener interface for receiving message events. The class that is interested in processing a
* message event implements this interface.
*
* @see MessageEvent
*/
public interface MessageListener
{
/**
* Message received notification.
*
* @param data the data
* @throws IOException Signals that an I/O exception has occurred.
*/
public void MessageReceived( byte[] data ) throws IOException;
/**
* Connection closed notification.
*/
public void closed();
}
现在,您可以根据需要创建任意数量的实现,例如:
public class A
{
private ArrayList<Listener> listeners = new ArrayList<Listener>();
public A()
{
// stub
}
public void addListener(Listener listener)
{
this.listeners.add(listener);
}
public void removeListener(Listener listener)
{
this.listeners.remove(listener);
}
public interface Listener
{
// stub
}
}
public class B implements A.Listener
{
private A instanceOfA;
public B()
{
this.instanceOfA = new A();
this.instanceOfA.addListener(this);
}
}
public class MessageListenerImpl implements MessageListener
{
/** The driver. */
private DeviceClassInternal driver;
/** The log. */
private Log log;
/**
* Instantiates a new TCP message listener.
*
* @param driver the driver
* @param log the log
*/
public MessageListenerImpl( DeviceClassInternal driver, Log log )
{
this.driver = driver;
this.log = log;
}
/** {@inheritDoc} */
@Override
public void MessageReceived( byte[] data ) throws IOException
{
log.info( "data received: {1}", new String( data ) );
driver.dataReceived( data );
}
@Override
public void closed()
{
log.info( "{0}: Connection closed", physicalConnection.getName() );
driver.closed();
}
}
最后,您可以创建它并将此侦听器注册到所需的对象:
MessageListenerImpl listener = new MessageListenerImpl( connection, log ); // create the listener and you're good to go.
physicalConnection.registerListener( listener ); // Just some object with a register function.
在实践中,解决了这一问题,这样观察员
B
就不会直接引用A
。它应该是一个小型类,专注于处理其事件的唯一任务,并且不应该与a
有任何直接耦合。事实上,这正是observer模式的要点:一种灵活的方式,可以向a
添加代码,而不必依赖它
如果事件处理程序必须观察作为事件源的对象(
A
),那么事件回调方法应该声明一个A
类型的参数,以便它可以以无状态的方式传递给处理程序,仅在事件处理期间使用,之后不保留。这一切都取决于特定的用例。
正如其他人所指出的,在许多情况下,您不需要在侦听器中保存引用,而是依赖于在事件本身中传递引用。但这并不总是足够的——例如,您可能希望根据其他事件取消注册自己,并且您需要原始引用来调用removeListener
但这是一个更一般的问题——如果你仍然在其他地方引用一个,那么不管你的监听器设计如何,它都不会得到GCed。另一方面,如果您在程序中到处都忘记了对A的引用,并且也没有保存对B的引用,那么它们无论如何都会被垃圾收集—循环引用不会阻止java()中的垃圾收集
我过去也有过类似的情况,但我关心的是B是GCD而不是A。当外部对B的所有引用都丢失时,没有人注销它,但A仍然会在每次事件中通知它。在java的UI框架中,如果您没有完全清理干净,那么以这种危险的侦听器结束是很常见的(人们通常不会这样做,因为他们认为以图形方式摆脱UI组件并忘记对它的所有引用就足够了——例如,一些全局键盘处理程序的侦听器或某些东西仍然将所有内容保存在强可访问集中)
对于UI框架,您没有机会更改核心代码,但使用自己的代码,您可以尝试使侦听器列表具有弱引用而不是强引用,并在需要时对每个通知进行清理。这样做的副作用是您必须将侦听器引用从代码的其他位置保留下来,但这是一种很好的做法无论如何(在另一种情况下,您应该手动取消它们的注册)-如果您不这样做,使用弱引用,您将突然停止在随机时间(在运行了几个gc循环之后)被回调
无论如何,您首先需要理解(并告诉我们),您如何想象A和B的生命周期,以及为什么在A可能已经消失后B将被强引用。您可以始终使B->A引用也变弱,但请首先确保完全理解您对在什么时候忘记什么的期望。如果您想防止循环引用,只需在类之间建立关系即可(这里
A
和B
是单向的,而不是双向的。或者添加一个调停者(但最后可能很难看)…编辑:这个问题属于@KarelG,我不能让它是单向的,因为B需要能够调用A上的方法(此外,如果B没有保留对A的引用,那么A将被销毁)。我无法删除A与B之间的通信,因为B需要知道A中何时发生了某些事情。这与我正在做的有什么不同?我使用的是一个接口。最终,您仍然会让主体保留对观察者的引用(通过接口),观察者保留对主题的引用。不,你不应该保留任何引用。你需要一个独立的接口,该接口的实现将负责处理你抛出的内容。看看我的上一个代码示例。我正在创建一个新的侦听器对象,并将其传递给我的对象a。我的侦听器没有实现a,所以它没有引用。A是由B创建的,用于处理网络内容。这两个类的链接非常紧密。例如,在初始化过程中,B调用instanceOfA.connect()
,稍后当用户想要断开B调用instanceOfA.disconnect()时
在关闭UI之前,或者如果用户执行了导致发送网络活动B调用的操作instanceOfA.sendSomething()
,等等。因此B必须有对a的引用。a也必须有对B的引用,以便a可以告诉B何时收到响应。我看不到任何方法将B的其余部分与B中处理事件的部分与a分开。在这种情况下,对观察者的一般支持是没有意义的。您只有两个紧密耦合的类。如果B可以