如何在保持一致引用的同时复制对象?

时间:2015-06-06 10:36:46

标签: java oop copy

我想为我正在进行的游戏实现一个检查点系统,为此我需要复制一个级别中的所有可变对象,以便创建一个级别的独立副本。在一个简化的例子中,我的类看起来像这样:

public class GameObject
{
    ...
    private Level level;
    ...
}

public class Level
{
    ...
    private List<GameObject> gameObjects;
    ...
}

但是有一个问题:当我想要复制一个级别及其对象时,引用变得不一致。例如,我可以深度复制我的Level-instance并深度复制所有gameObjects。但是当我这样做时,GameObject中的Level-reference不再指向“正确”(新)级别。在这种情况下,我可以为每个对象调用一个方法并重置其级别。但随着越来越多类型的对象被包含,这将变得越来越复杂,因为对象可能或可能没有自己创建的对象集,然后这些引用也必须更新等等。

对于这种情况,有什么类似设计模式的东西,或者特定于Java的东西会使这个问题变得更容易吗?

修改

我刚才有一个想法:如何为每个GameObject创建一些包装类,如下所示:

public class GameObjectWrapper
{
    ...
    private long gameObjectID;
    private Level level;
    ...
}

这将作为对所有需要引用的对象的引用。 每个GameObject都会得到一个唯一的id,level会有一个地图将每个id链接到一个实际的GameObject(类似GameObject Level.getObject(long id))。虽然这可能会奏效,但我仍然觉得必须有更好的解决方案。

编辑:进一步澄清

似乎我没有把我的问题弄清楚,所以我会简化并概括一点:

我有两个示例对象(objectAobjectB):objectA包含对objectB的引用,objectB包含对objectA的引用}。

这样的事情:

public class MyObject
{
   private MyObject partner;

   ...

   public MyObject shallowCopy()
   {
        return new MyObject(partner);
   }
}

我想复制两者。通过浅拷贝,我得到两个新对象:newObjectAnewObjectBnewObjectA存储的引用(应该指向newObjectB)仍然指向原始objectB,反之亦然。

MyObject objectA = new MyObject(), objectB = new MyObject();

objectA.setPartner(objectB);
objectB.setPartner(objectA);

MyObject newObjectA = objectA.copy(); //newObjectA's partner now is objectB (instead of newObjectB)
MyObject newObjectB = objectB.copy(); //newObjectB's partner now is objectA (instead of newObjectA)

现在,我可以“只是”遍历我的所有对象并将旧对象映射到新对象,但这对我来说似乎很慢而且太复杂了。解决此问题的最简单,最有效的方法是什么?

注意:我考虑将此发布到gamedev.stackexchange.com,但我发现这更像是一个编程和设计特定问题,而不是游戏开发特定问题。

2 个答案:

答案 0 :(得分:3)

如果您只复制Level对象并希望其列表中的引用指向新副本,则无法在复制方法中循环它们。

这样的事情:

class Level {

    List<GameObject> gameObjects;

    Level copy() {
        Level level = new Level();
        level.gameObjects = new ArrayList<>(gameObjects.size());
        for (GameObject gameObject : gameObjects) {
            level.gameObjects.add(gameObject.copyWithReference(level));
        }
        return level;
    }
}

class GameObject {

    Level level;

    GameObject copyWithReference(Level level) {
        GameObject gameObject = new GameObject();
        gameObject.level = level;
        return gameObject;
    }
}

编辑:

您可以拥有gameObjects的容器:

class GameObjectSet {

    Level level;
    List<GameObject> gameObjects;

    GameObjectSet copy(Level newLevel) {
        GameObjectSet gameObjectSet = new GameObjectSet();
        gameObjectSet.level = newLevel;
        // copy all gameObjects and make them point to new gameObjectSet
        return gameObjectSet;
    }
}

现在,您可以在GameObjectSet课程和Level课程中同时参考GameObject

答案 1 :(得分:2)

检查点的经典解决方案是Memento pattern

也就是说,如果您想要持久检查点,Java序列化可能就是您所寻找的,因为除了对象状态(在同一个流中)之外它还保留了对象标识。这将是:

ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
    oos.writeObject(level);
}

byte[] checkpoint = baos.getBytes();

try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(checkpoint)) {
    Level level = (Level) ois.readObject();
    assert level == level.getGameObjects().get(0).getLevel();
}