开发者

How to track changes to an object graph?

I am working on some code that I want to detect when something in the object has changed. One of the easiest ways to track changes is to keep an old开发者_如何学C copy of the object around. However, it looks like getting a copy of the deep graph may be difficult. Here's what I tried:


public class Old{
    protected Old old;
    protected List stuff;

    //Needed for JUnit
    public Old(){
    }

    public Old(List stuff){
        this.stuff=stuff;
        old=this;
    }

    public void add(){
        stuff.add(2);
    }

    public void match(){
        System.out.printf("old:%d\nnew:%d\n",old.getStuff().size(),stuff.size());
    }

    public List getStuff(){
        return new ArrayList(stuff);
    }

    @Test
    public void testOld(){
        List list=new ArrayList();
        list.add(1);
        Old thing=new Old(list);
        thing.add();
        thing.match();
    }
}

The Output:

old:2
new:2

So, it appears that by default old=this does not create a deep copy. All I really want is to track changes to that list and potentially a graph of objects. What are some other simple options?


Depending on what your specific requirements are this might work

  1. control access to changing the object to setters (i.e. use proper encapsulation)
  2. add a revisions array which is an Array of Strings
  3. everytime a setter is called, or any other method that changes the object, call toString and store the result

this is very simple but might work for your needs and has the following pros:

  1. You could abstract it out into a base class for reuse.
  2. You could test string equality to not record a set that doesn't change anything. (compare the current change with the last element in the revisions array)
  3. You could get really fancy and have a property like 'recordChanges', so the recording only happens when true (which could be the default). This way if you are going to do a bunch of sets in a row you could record all the changes at once, instead of for each set. (i.e. create a startRecording and stopRecording method, and when you startRecording record the current state)

it has the following extra work:

  1. You would have to implement a solid toString and hashCode though. Which you might have to do anyway

  2. It could be slow if you call a bunch of sets in a row, but you could mitigate that with suggestion 3 above.

Finally, if you got really fancy, you could implement the Gang of Four Momento pattern. Which might be overkill for what you need (because it allows you to restore to a previous state), but would be awesome nonetheless.


You are comparing a ArrayList to a copy of the same ArrayList without changes. They are always going to have the same number of elements, regardless of whether or not the copy was deep.

Yes, in this case ArrayList holds references and will not create new objects if you pass a list in ArrayList's constructor instead it will have the same references to the objects.


You may consider using an Observer. You can create an Observer class that is notified by your objects when a change happens. Maybe you can keep the old and new values. Then you can add the observer to each object that you want tracked. You can read more about this option here and here.


You've only copied the pointer to the reference. Both list and old.stuff refer to the same object. Any changes made to list will be reflected in old.stuff and vice versa. You want to make a copy of the List, using Collections.copy. Then any changes to list will not be reflected in the your reference (to a difference List object) in old.stuff. Have a look at this answer for more details.


After people pointed out that old may be pointing to new, I added a copy constructor and things are looking much better.


public class Old{
    protected Old old;
    protected List stuff;

    //Needed for JUnit
    public Old(){
    }

    //Here's my new copy constructor.
    public Old(Old old){
        this.stuff=new ArrayList(old.getStuff());
        this.old=null;
    }

    public Old(List stuff){
        this.stuff=stuff;
        old=new Old(this);//Here I now call the copy constructor.
    }

    public void add(){
        stuff.add(2);
    }

    public void match(){
        System.out.printf("old:%d\nnew:%d\n",old.getStuff().size(),stuff.size());
    }

    public List getStuff(){
        return new ArrayList(stuff);
    }

    @Test
    public void testOld(){
        List list=new ArrayList();
        list.add(1);
        Old thing=new Old(list);
        thing.add();
        thing.match();
    }
}

Output:

old:1
new:2


Tracking changes of a list can be done using a ChangeTrackingUniqueList - It offers methods like "list.isChanged()", "list.getDeleted()", "list.getAdded()" and you can even revert the list with "list.revert()". It does however not detect changes to the objects in the list. It only detects if the list itself is changed by adding, deleting or replacing objects.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜