I'm writing a Java class to manage a hex map ( class GameMapImpl implements GameMap
) that contains Cell
objects. Cell objects are saved in a HashMap<Hex,Cell>
, where the key is the position on the hex map.
The whole thing used to be very tightly coupled between cells and GameMap, with a cyclic dependency between the two, but I'm trying to refactor it to allow more easy testing.
(code below is simplified)
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
}
Each Cell
needs to have a Hex position
field so it can look up its own position in the GameMap
. In addition, Cell
objects need to have a reference to the owning GameMap
, to perform common useful operations such as looking for their neighbours.
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
}
}
The GameMap
is built in a GameMapBuilder
, which provides a Set<Cell>
to the GameMap
constructor.
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;
}
}
As you can probably see, the last snippet of code is wrong, because i'm referencing a non-existent gameMap
variable. Here lies my problem: if I create the GameMap
after initializing the cells, I cannot pass it in the Cell
's constructor. If I do the opposite, I cannot pass the Set<Cells>
to the gameMap
in order to initialize correctly.
Does any more experienced programmer have an idea on how to decouple correctly these two classes?
Alternatively, I can go back to the previous tightly coupled design and just assume that Cells exist only when a GameMap creates them. Being these small objects and contained in the same package, it wouldn't be that big of a deal.
I think the problem here is that you use a builder. Basically what you did in the builder is took code away from the GameMap
constructor. If you were to put this code in the GameMap
constructor you can give this
to the Cell
constructor.
If you still want to keep the builder code separated from the GameMap
code you can make a static method on the builder that the GameMap
constructor calls and pass this
(the GameMap
object) as a parameter.
Use setter for passing GameMap
into Cell
not constructor. You can make it package-protected to hide it from other code and call setter in GameMap
constructor or in another loop in GameMapBuilder
. All known DE-frameworks use setters to solve circular dependencies.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.