我有两个类别(A和B),它们在以下意义上相互依赖:
每个班级都有一个执行某些动作的方法
每个类的动作取决于另一个类的动作。
因此,如果用户调用A类的动作,它应该自动调用
B级的行动。
相反的方式。但是应该防止无限循环。
我找到了一些处理这个问题的代码,但它似乎是一个 对我来说有点愚蠢:锁定阻止了无限循环。
import java.util.concurrent.locks.*;
import static java.lang.System.*;
import org.junit.*;
public class TEST_DependentActions {
static class A {
private B b = null;
private final ReentrantLock actionOnBLock = new ReentrantLock();
public void setB(B b) {
this.b = b;
}
public void actionOnB() {
if (!actionOnBLock.isLocked()) {
actionOnBLock.lock();
b.actionOnA();
actionOnBLock.unlock();
}
}
}
static class B {
private A a = null;
private final ReentrantLock actionOnALock = new ReentrantLock();
public void setA(A a) {
this.a = a;
}
public void actionOnA() {
if (!actionOnALock.isLocked()) {
actionOnALock.lock();
a.actionOnB();
actionOnALock.unlock();
}
}
}
@Test
public void test1()
throws Exception {
out.println("acting on class A first:");
A a = new A(); B b = new B();
a.setB(b); b.setA(a);
a.actionOnB();
}
@Test
public void test2()
throws Exception {
out.println("acting on class B first:");
A a = new A(); B b = new B();
a.setB(b); b.setA(a);
b.actionOnA();
}
}
输出如下:
acting on class A first:
A : calling class B's action.
B : calling class A's action.
acting on class B first:
B : calling class A's action.
A : calling class B's action.
嗯,它有效,但似乎不是最佳解决方案。
你怎么做呢? 是否存在处理此类问题的模式?
修改
我想知道它一般
但是,假设我有一个容器,其中包含多个元素。
Container提供了方法 remove(someElement)和
Element还提供了一种方法 removeMe()
两种方法都相互依赖,但不能连接到一种方法
方法,因为这两种方法另外执行一些内部的东西,这是唯一的
每个班级都可以访问内部。
答案 0 :(得分:8)
我会通过重新思考逻辑来处理这个问题。循环依赖通常表示某些东西有点......关闭。如果没有更深入地了解确切的问题,我就不能更具体了。
答案 1 :(得分:2)
您可以将其中一种方法设为私有/内部。 这应该确保客户端代码只能调用另一个,并且您始终知道调用的工作方式。
或者,使用容器/元素示例,如下所示:
public class Container
{
public void Remove(Element e)
{
e.RemoveImplementation();
RemoveImplementation();
}
// Not directly callable by client code, but callable
// from Element class in the same package
protected void RemoveImplementation()
{
// Mess with internals of this class here
}
}
public class Element
{
private Container container;
public void Remove()
{
RemoveImplementation();
container.RemoveImplementation();
}
// Not directly callable by client code, but callable
// from Container class in the same package
protected void RemoveImplementation()
{
// Mess with internals of this class here.
}
}
我不确定这种模式是否有通用名称。
答案 2 :(得分:1)
所呈现的解决方案在给定方案中看起来完全可以接受。但这实际上取决于行为意味着这种行为是否正确。如果这正是您想要的行为,那么我不确定您是否有问题。
<强>附录:强>
在这种情况下,我会说,将“额外的东西”拆分成一个单独的方法,这样容器上的remove
可以在没有递归回调的情况下调用元素上的“额外东西”。类似地,容器上的额外内容可以分开,以便removeMe
可以调用仅执行非递归内容的容器上的方法。
答案 3 :(得分:1)
我的建议是减少对代码的思考,更多地考虑设计。可以将共享操作抽象为一个新类,这两个类都可以与之通信吗?是否在两个类中错误地共享了功能?
今年在Uni,他们引入了“代码味道”的概念,这是代码需要重新分解的一堆“直觉反应”。也许可以帮忙吗?
如需概述,请尝试: Wikipedia Code Smells或this book。
也许您可以告诉我们更多关于您在代码中尝试表示的内容?
我希望这会有所帮助。
答案 4 :(得分:1)
我认为这可以通过类的全局布尔值来解决(当然,使其成为实例变量)。它检查是否([布尔变量])以及它是否为真;它运行对另一个方法的调用,如果它是假的,它不会。就在if语句中,将check设置为false。然后在每个方法结束时将其设置为true。
这就是我如何做到的。
答案 5 :(得分:0)
如果引发异常,您的实施将无法解锁。锁可能很昂贵,稍微轻一点的方法是使用AtomicBoolean
private final AtomicBoolean actionOnBLock = new AtomicBoolean();
public void actionOnB() {
if (!actionOnBLock.getAndSet(true))
try {
b.actionOnA();
} finally {
actionOnBLock.set(false);
}
}
正如其他人所建议的那样,更好的方法是编写代码,这样就不必调用另一个代码。相反,你可以有一个调用A和B的方法,这样两个对象就不需要彼此了解了。
public class AB {
private final A a;
private final B b;
public AB(A a, B b) {
this.a = a;
this.b = b;
}
public void action() {
a.action(); // doesn't call b.
b.action(); // doesn't call a.
}
}
答案 6 :(得分:0)
正如其他人所说,我认为在大多数情况下你应该尽量避免这种情况并重构你的代码,这样就不会出现问题。但是在某种情况下,这可能不是一种选择,也可能是完全不可避免的。最通用的方法是确保在方法A调用方法B之前(反之亦然),它必须确保A的对象处于对A的附加调用立即返回并因此导致无操作的状态。这非常抽象,很难应用于具体的类和实现。
以容器和元素类(例如,C和E)为例,其中C有一个方法C.remove(E e),E有一个方法E.remove(),你可以像这样实现它:
class C:
elements = [...] // List of elements
remove(e):
if not elements.contains(e):
return
elements.remove(e)
// Do necessary internal stuff here...
// and finally call remove on e
e.remove()
class E:
container = ... // The current container of E
remove():
if container is none:
return
c = container
container = none
// Do necessary internal stuff here...
// and finally call remove on c
c.remove(this)
答案 7 :(得分:0)
函数fooA()再次调用fooB()和fooB()调用fooA()是一个有效的递归。 递归并不总是必须与调用自身的函数相同。
那么,我们如何防止递归函数无限循环? 通过终止条件对吗?
我想这就是要走的路。但是,我同意其他人关于重新思考设计并避免这种递归的评论。
Simon的解决方案也依赖于终止条件,如
if not elements.contains(e):
return
和
if container is none:
return