对象池模式-关注点分离-封装:谁负责删除实例



情况

在一次开发过程中,我实现了名为对象池模式的设计模式。基本上,这意味着我们的类有一个静态的公共方法和一个静态保护的属性。该属性承载所有实例,如果提供了特定的对象键,则静态公共方法可以检索这些实例。我使用一个名为get_instance的方法将实例检索到本地引用变量中,然后调用它上的其他方法。在我们的例子中,包装函数模块调用get_instance,然后调用该实例上的一些方法。在特定情况下,某个实例的生存期结束。因此,它必须从对象池中删除。到目前为止,我在一个实例方法中执行此操作,在该方法中,对象将自己从池(表)中删除。

假设

函数模块中的外部引用本应无效,因为它确实引用了不存在的对象。如果我假设正确的话,垃圾收集器现在应该携带这个孤立引用。(通常情况下,情况正好相反:未引用的对象通常会被GC杀死。)在这种情况下,我仍然认为函数模块中的本地引用也应该被收集。因为它所引用的对象已从对象池中删除。尽管如此,直到现在,我仍然称之为Free lr_myreference。一位同事说,在调用Free lr_myreference之前,最好实现一个静态的公共方法,它不会对引用进行操作,而只是杀死池的入口。

问题

一般来说,我在想:谁负责从池中删除对象?其他引用表中的条目("原始引用")?

我经常使用与您描述的模式完全相同的模式,最好使用一些哈希表作为工厂/管理器/池类的静态属性。

就我个人而言,我尽量避免那些需要明确地最终确定的对象。根据我的经验,随着应用程序变得越来越复杂,总会有人忘记最终确定。这反过来可能会导致任何数量的不良副作用,这些副作用是出了名的难以调试。我试图遵循一个基本假设,即垃圾收集器将最清楚何时删除对象,一旦对象消失,就只有这些了。只要任何人(函数组、另一个类,都无关紧要)保留对对象的引用,它就仍然在使用中,不会被gc'ed。除了需要经常显式解构对象实例的控制框架之外,这种方法也非常有效。

这种方法的明显缺点是,除非正确实现,否则对象池将倾向于增长。我的实例哈希表不包含对实例的硬引用;相反,我使用CL_ABAP_WEAK_REFERENCE来跟踪托管对象。这允许垃圾收集器删除所有未在其他地方使用的托管实例。当然,这也意味着您需要通过其他直接引用来跟踪您仍然需要的实例(例如,使用一个封装ENQUEUE/DEQUEUE调用的锁对象,同时用作锁令牌——该对象的当前所有者也有责任再次释放锁)。

注意:在广泛使用弱引用时,必须避免一个相当常见的结构错误我不久前写了一篇SCN文章,其中包含一个(计数器)示例。一句话:如果管理对象(在您的示例中,池类)是通过弱引用引用的,请确保托管实例对管理器有硬引用,否则您可能会意外地得到代表同一事物的多组托管实例。

本质上,您是在将对象的所有权从池中临时转移(或至少借出)给get_instance的调用方,因此池在知道新所有者已经使用完对象之前无法杀死对象或将其从池中删除。如果是这样,则当前所有者可能会留下一个无效对象。

因此,为了使池正确地完成其工作,您需要将对象返回到池中(或告诉它您已经完成了使用),以便池可以知道哪些对象当前未使用,并可以安全地销毁它们。

我会有一个return_instance对象来执行此操作。

如果您有多个客户端在调用get_instance,并且您为它们提供了相同的实例,那么您需要按引用计数进行跟踪,否则您可以将引用保留在两个列表中,一个用于可用对象,另一个用于当前借出的对象,并且在调用get_instancereturn_instance时只需在列表之间切换。

客户端不应该负责销毁对象,否则拥有池有什么意义?只是有一个工厂代替。。。

不久前,我问了一个稍微相关的问题,这影响了我的思维

最新更新