【问题标题】:Breaking cyclic dependency in constructor在构造函数中打破循环依赖
【发布时间】:2015-08-30 01:00:49
【问题描述】:

我正在编写一个 Java 类来管理包含 Cell 对象的十六进制映射 (class GameMapImpl implements GameMap)。单元格对象保存在HashMap<Hex,Cell> 中,其中键是十六进制图上的位置。

过去,cell 和 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&lt;Cell&gt;

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;
    }
}

您可能会看到,最后一个 sn-p 代码是错误的,因为我引用了一个不存在的 gameMap 变量。这是我的问题:如果我在初始化单元格后创建GameMap,我不能在Cell 的构造函数中传递它。如果我反其道而行之,我将无法将 Set&lt;Cells&gt; 传递给 gameMap 以正确初始化。

有没有经验丰富的程序员知道如何正确解耦这两个类?

或者,我可以回到之前的紧密耦合设计,并假设 Cell 仅在 GameMap 创建它们时才存在。作为这些小物件并包含在同一个包装中,这没什么大不了的。

【问题讨论】:

    标签: java dependency-injection dependencies circular-dependency decoupling


    【解决方案1】:

    使用 setter 将 GameMap 传递给 Cell 而不是构造函数。您可以使其受包保护以将其隐藏在其他代码中,并在GameMap 构造函数或GameMapBuilder 的另一个循环中调用setter。所有已知的 DE 框架都使用 setter 来解决循环依赖关系。

    【讨论】:

      【解决方案2】:

      我认为这里的问题是您使用了构建器。基本上,您在构建器中所做的是将代码从 GameMap 构造函数中取出。如果您要将这段代码放在GameMap 构造函数中,您可以将this 提供给Cell 构造函数。

      如果您仍然希望将构建器代码与GameMap 代码分开,您可以在构建器上创建一个静态方法,由GameMap 构造函数调用并将thisGameMap 对象)作为参数传递.

      【讨论】:

      • 这是我在重构之前所拥有的,但我想做一些事情来打破循环依赖。
      猜你喜欢
      • 1970-01-01
      • 2011-04-08
      • 1970-01-01
      • 2015-10-13
      • 2020-01-29
      • 1970-01-01
      • 2017-10-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多