打破构造函数中的循环依赖
Breaking cyclic dependency in constructor
我正在编写 Java class 来管理包含 Cell
个对象的十六进制映射 (class GameMapImpl implements GameMap
)。单元格对象保存在HashMap<Hex,Cell>
中,其中键是十六进制图上的位置。
Cells 和 GameMap 之间的整体关系过去非常紧密,两者之间存在循环依赖关系,但我正在尝试重构它以允许更轻松的测试。
(以下代码已简化)
public interface GameMap {
void hasCell(Hex hex);
void getCell(Hex hex);
}
class GameMapImpl implements GameMap
{
private Map<Hex, Cell> cellMap;
GameMapImpl(Set<Cell> cells) {
cellMap = new HashMap<>();
for (Cell cell : cells) {
// check some conditions on the cells
// ensure the map is correct, e.g. only one cell
// of a particular type
// ensures that the cellMap key is always the cell's position.
cellMap.put(cell.position(), cell);
}
}
//obvious implementation for hasCell and getCell methods
}
每个 Cell
都需要有一个 Hex position
字段,以便它可以在 GameMap
中查找自己的位置。此外,Cell
个对象需要引用所属 GameMap
,以执行常见的有用操作,例如寻找它们的邻居。
public class Cell {
final Hex position;
final GameMap gameMap;
Cell(Hex position, GameMap gameMap) {
this.position = position;
this.gameMap = gameMap;
}
public Set<Cell> getNeighbours() {
//perform operation that needs both gameMap and position
}
}
GameMap
内置于 GameMapBuilder
中,它为 GameMap
构造函数提供 Set<Cell>
。
public class GameMapBuilder {
public GameMap build(...) { // args omitted for simplicity
Set<Cells> cells = new HashSet<>();
for (... : ...)
{
Hex position = calculatePosition(...);
Cell cell = new Cell(position, gameMap);
}
GameMap gameMap = new GameMap(cells);
return gameMap;
}
}
正如您可能看到的那样,最后一段代码是错误的,因为我引用了一个不存在的 gameMap
变量。这就是我的问题:如果我在初始化单元格后创建 GameMap
,我无法将它传递到 Cell
的构造函数中。如果我做相反的事情,我无法将 Set<Cells>
传递给 gameMap
以便正确初始化。
有没有更有经验的程序员知道如何正确解耦这两个 classes?
或者,我可以回到之前的紧耦合设计,假设只有当 GameMap 创建 Cell 时 Cell 才存在。作为这些小物件并包含在同一个包裹中,这没什么大不了的。
我认为这里的问题是您使用了生成器。基本上,您在构建器中所做的就是从 GameMap
构造函数中获取代码。如果要将此代码放在 GameMap
构造函数中,则可以将 this
赋给 Cell
构造函数。
如果您仍想将生成器代码与 GameMap
代码分开,您可以在 GameMap
构造函数调用的生成器上创建一个静态方法并传递 this
( GameMap
对象)作为参数。
使用 setter 将 GameMap
传递给 Cell
而不是构造函数。您可以使其受包保护以将其隐藏在其他代码中,并在 GameMap
构造函数或 GameMapBuilder
中的另一个循环中调用 setter。所有已知的 DE 框架都使用 setters 来解决循环依赖。
我正在编写 Java class 来管理包含 Cell
个对象的十六进制映射 (class GameMapImpl implements GameMap
)。单元格对象保存在HashMap<Hex,Cell>
中,其中键是十六进制图上的位置。
Cells 和 GameMap 之间的整体关系过去非常紧密,两者之间存在循环依赖关系,但我正在尝试重构它以允许更轻松的测试。
(以下代码已简化)
public interface GameMap {
void hasCell(Hex hex);
void getCell(Hex hex);
}
class GameMapImpl implements GameMap
{
private Map<Hex, Cell> cellMap;
GameMapImpl(Set<Cell> cells) {
cellMap = new HashMap<>();
for (Cell cell : cells) {
// check some conditions on the cells
// ensure the map is correct, e.g. only one cell
// of a particular type
// ensures that the cellMap key is always the cell's position.
cellMap.put(cell.position(), cell);
}
}
//obvious implementation for hasCell and getCell methods
}
每个 Cell
都需要有一个 Hex position
字段,以便它可以在 GameMap
中查找自己的位置。此外,Cell
个对象需要引用所属 GameMap
,以执行常见的有用操作,例如寻找它们的邻居。
public class Cell {
final Hex position;
final GameMap gameMap;
Cell(Hex position, GameMap gameMap) {
this.position = position;
this.gameMap = gameMap;
}
public Set<Cell> getNeighbours() {
//perform operation that needs both gameMap and position
}
}
GameMap
内置于 GameMapBuilder
中,它为 GameMap
构造函数提供 Set<Cell>
。
public class GameMapBuilder {
public GameMap build(...) { // args omitted for simplicity
Set<Cells> cells = new HashSet<>();
for (... : ...)
{
Hex position = calculatePosition(...);
Cell cell = new Cell(position, gameMap);
}
GameMap gameMap = new GameMap(cells);
return gameMap;
}
}
正如您可能看到的那样,最后一段代码是错误的,因为我引用了一个不存在的 gameMap
变量。这就是我的问题:如果我在初始化单元格后创建 GameMap
,我无法将它传递到 Cell
的构造函数中。如果我做相反的事情,我无法将 Set<Cells>
传递给 gameMap
以便正确初始化。
有没有更有经验的程序员知道如何正确解耦这两个 classes?
或者,我可以回到之前的紧耦合设计,假设只有当 GameMap 创建 Cell 时 Cell 才存在。作为这些小物件并包含在同一个包裹中,这没什么大不了的。
我认为这里的问题是您使用了生成器。基本上,您在构建器中所做的就是从 GameMap
构造函数中获取代码。如果要将此代码放在 GameMap
构造函数中,则可以将 this
赋给 Cell
构造函数。
如果您仍想将生成器代码与 GameMap
代码分开,您可以在 GameMap
构造函数调用的生成器上创建一个静态方法并传递 this
( GameMap
对象)作为参数。
使用 setter 将 GameMap
传递给 Cell
而不是构造函数。您可以使其受包保护以将其隐藏在其他代码中,并在 GameMap
构造函数或 GameMapBuilder
中的另一个循环中调用 setter。所有已知的 DE 框架都使用 setters 来解决循环依赖。