You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java海战开发:如何从持有子对象引用的类访问父对象

从持有舰段引用的单元格访问所属战舰的几种方案

我之前开发格子类策略游戏时也遇到过类似的需求,针对你这个海战游戏的场景,有几个实用的解决方案,按推荐程度排序:

方案1:给舰段(ShipSegment)添加父战舰引用(最推荐)

这是最直观也最符合面向对象设计的方式——既然每个舰段都属于某一艘战舰,那就在ShipSegment类里直接持有所属Ship的引用,在创建舰段时就把父战舰传进去初始化。这样单元格拿到ShipSegment后,直接调用 getter 就能获取到对应的战舰。

示例代码:

public class ShipSegment {
    // 用final确保引用不会被意外修改
    private final Ship parentShip;
    private int x;
    private int y;

    // 构造方法传入父战舰
    public ShipSegment(Ship parentShip, int x, int y) {
        this.parentShip = parentShip;
        this.x = x;
        this.y = y;
    }

    // 提供getter方法
    public Ship getParentShip() {
        return parentShip;
    }

    // 你的place()方法
    public void place(int newX, int newY) {
        this.x = newX;
        this.y = newY;
        // 其他逻辑...
    }
}

在创建战舰和舰段时,只需要这样关联:

// 创建一艘战舰
Ship destroyer = new Ship();
// 创建舰段时传入所属战舰
ShipSegment seg1 = new ShipSegment(destroyer, 2, 3);
ShipSegment seg2 = new ShipSegment(destroyer, 2, 4);
// 将舰段添加到战舰的管理列表(如果需要的话)
destroyer.addSegment(seg1);
destroyer.addSegment(seg2);

之后单元格要获取战舰时,直接通过舰段引用调用:

// 假设你的Cell类有getShipSegment()方法
ShipSegment segment = cell.getShipSegment();
if (segment != null) {
    Ship parentShip = segment.getParentShip();
    // 接下来处理父战舰的逻辑...
}

这个方案的优点是逻辑清晰、访问效率高,而且舰段和战舰的关联关系是显式的,后期维护起来也方便。


方案2:使用映射表(Map)关联舰段与战舰

如果出于某些原因(比如不想让ShipSegment直接依赖Ship),可以用一个Map<ShipSegment, Ship>来存储舰段到战舰的映射关系。这个映射可以放在游戏的核心管理类里,比如SeaBattleGame。

示例代码:

public class SeaBattleGame {
    // 维护舰段到战舰的映射
    private Map<ShipSegment, Ship> segmentToShipMap = new HashMap<>();

    // 创建战舰的方法
    public Ship createShip(List<Point> segmentCoords) {
        Ship ship = new Ship();
        for (Point coord : segmentCoords) {
            ShipSegment segment = new ShipSegment(coord.x, coord.y);
            ship.addSegment(segment);
            // 将舰段和战舰关联存入映射
            segmentToShipMap.put(segment, ship);
        }
        return ship;
    }

    // 通过舰段获取战舰的方法
    public Ship getShipBySegment(ShipSegment segment) {
        return segmentToShipMap.get(segment);
    }
}

然后单元格获取战舰时:

ShipSegment segment = cell.getShipSegment();
if (segment != null) {
    Ship parentShip = game.getShipBySegment(segment);
    // 处理逻辑...
}

这个方案的好处是解耦了ShipSegment和Ship,但需要注意映射的生命周期管理:当战舰被销毁(比如被击沉)时,要记得从映射中移除对应的所有舰段条目,避免内存泄漏。


方案3:遍历战舰列表查找(不推荐)

如果以上两种方案都不适用,还可以通过遍历所有战舰的舰段列表来找到对应的战舰,但这个方法的效率很低,尤其是当游戏中的战舰数量较多时,不建议使用。

示例代码:

public class Ship {
    // 维护所有战舰的全局列表(或者放在游戏管理类里)
    private static List<Ship> allShips = new ArrayList<>();
    private List<ShipSegment> segments = new ArrayList<>();

    public Ship() {
        allShips.add(this);
    }

    // 静态方法:通过舰段查找所属战舰
    public static Ship findShipForSegment(ShipSegment target) {
        for (Ship ship : allShips) {
            if (ship.segments.contains(target)) {
                return ship;
            }
        }
        return null;
    }
}

使用时:

ShipSegment segment = cell.getShipSegment();
if (segment != null) {
    Ship parentShip = Ship.findShipForSegment(segment);
    // 处理逻辑...
}

这个方案的缺点很明显:每次查找都要遍历所有战舰和它们的舰段,性能差,而且全局列表的管理也容易出问题(比如战舰销毁时忘记从列表移除)。


总结

优先选择方案1,它最符合面向对象的设计原则,代码简洁且易于维护。如果需要解耦,可以考虑方案2,但要做好映射的生命周期管理。方案3只适合极小规模的游戏场景,尽量避免使用。

内容的提问来源于stack exchange,提问作者Axel Köhler

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:39:54