我有两个类:
class A {
final B b;
A(final B b) {
this.b = b;
}
void doIt() {
b.doSomething();
}
void doSomething() {
// TODO Auto-generated method stub
}
}
和
class B {
final A a;
B(final A a) {
this.a = a;
}
void doIt() {
a.doSomething();
}
void doSomething() {
// TODO Auto-generated method stub
}
}
无法实例化它们中的任何一个。
我可以使用 setter,例如:
class B {
A a;
B() {
}
void setA(final A a) {
this.a = a;
}
void doIt() {
a.doSomething();
}
void doSomething() {
// TODO Auto-generated method stub
}
}
但在这里我需要检查 a
正在null
在 doIt()
并处理此案。这不是世界末日,但也许
有更聪明的方法吗?
或者这可能甚至是一般的反模式,并且首先是架构出了问题?
问题的根源:
我有一个从中加载实体的数据库。我在加载后缓存它们,并使用此缓存在类型之间建立双向关系。这是必要的,因为当我加载 A
的多个实例时,他们应该(在这种情况下)具有 B
的所有相同实例.因此 B 需要在之前实例化并重新使用。但是B
也与所有这些有关a
s,因此是循环依赖。
简而言之,循环依赖是由数据库中的双向关系引起的。我曾经尽可能避免使用它们,但除了此处描述的问题之外,双向关系不存在“现实世界”问题,事实上这是很自然的事情。
所以也许问题应该是如何将双向关系从关系数据库正确映射到 oop 世界?
更具体的例子:
为了建立双向关系,我从缓存中加载实例,这样当实体 ID 相同时我就有相同的实例:
interface Cache<T>
interface CacheA extends Cache<A>
interface CacheB extends Cache<B>
class CacheManager {
final CacheA cacheA;
final CacheB cacheB;
CacheManager(final DatabaseAccess databaseAccess) {
cachA = new CacheA();
cachB = new CacheB();
cacheA.setEntityLoader(id -> new SimpleAttachedA(id, databaseAccess, cacheB));
cacheB.setEntityLoader(id -> new SimpleAttachedB(id, databaseAccess, cacheA));
}
}
A
的实例如果与 B
的关系将访问此缓存被访问。反之亦然。
我知道最好是 CacheA
和 CacheB
将是同一个对象,从那时起我可以创建缓存并将其传递给 A
的所有实例和 B
.
CacheA
和 CacheB
曾经是一样的,即Cache
.我选择将它们分开,这样我就可以使用通用类并删除大量重复代码。
CacheManager
无法实现 CacheA
和 CacheB
, 如果它们都扩展相同的通用接口(interface) Cache<T>
但不同类型的 T
.
因此CacheManager
使用组合而不是继承。所以我最终得到两个缓存,需要互相访问才能实现A
的双向关系和 B
.
最佳答案
循环依赖通常是糟糕设计的标志。在某些情况下,您无法避免循环依赖,但您应该始终考虑其他解决方案。当您有循环依赖时,您很可能会更改其中一个类,并且还必须更改另一个类。当您的循环包含两个以上的类时,这可能需要大量工作。还出现了您提到的问题,“我需要另一个类来实例化其中一个”。
由于您的类(class)似乎是虚拟类(class),因此我无法给出真正好的建议,但仍然有一些一般性的要点。
类应该具有高内聚和低耦合。意思是,类应该尽可能少地依赖另一个类(低耦合),而每个类都应该做一个功能,并且所有的功能(高内聚)。
当你有两个相互依赖的类时,这通常是低内聚的标志,因为一部分功能在类 A
中,另一部分在类 B
。在这种情况下,您应该考虑将两个类合并为一个类。
另一方面,当您尝试为该合并类找到一个类名并想出类似 ThisAndThatDoer
的东西时,您应该将它们分成两个类 ThisDoer
和ThatDoer
,因为这是低内聚的标志。当您的原始类再次相互依赖时,您可以创建一个新类 Executor
来连接这两个类。但这很快就会成为一个神类,这也是一种反模式。所以你应该小心这一点。
总而言之,我建议考虑您的类设计并找到一种方法至少在一个方向上消除依赖性。我希望这有助于解决您的问题。
关于java - 类型 A 和 B 之间的循环依赖,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45529749/