RSS LinkedIn Twitter

Detecting Changes in an Object

July 25th, 2009 Categories: Actionscript, Design Patterns, Flex 3

One of the projects I’m working on right now requires me to detect when changes to an object have occurred, so as to enable or disable some “OK”, “Apply” and “Cancel” buttons in the appropriate context.  For the longest time I’ve been thinking I was going to implement this with some sort of data binding, but as it’s distilled on my mind over the past few weeks I’ve decided that wasn’t the right approach.  Here are a few of the requirments/factors that played into that decision:

Background info: the gist of this app is that there’s a datagrid whose items can be loaded into a dialog for editing.

  1. The changes don’t affect the actual object in the model or database being edited until “Apply” or “OK” is pressed (i.e. they can always cancel out of the operation, regardless of any changes)
  2. If a user edits a field, I want that to register as a change.  If they change the field back to what it was, I don’t want that registered as another change, it needs to be as if it had never been changed (i.e. “OK” is not enabled).  This is why I ruled out a Binding approach.
  3. The objects being compared are pretty complex objects.  I didn’t want to have to go and manually compare each property, the properties of composed objects, and the properties of objects nested in collections.  The time to implement this, and the amount of time it would take to compare the object with its sentinel after each edit seemed to rule this option out.

I became pretty familiar with ByteArrays last year during a computer security class (I implemented my AES, RSA, etc. labs using ActionScript) and so I got the idea of serializing these objects to a ByteArray, and then comparing them.  After I came up with the idea, I googled it and realize I’m hardly the first to come up with it, but I’m still going to share it.

First, I built a method to convert an object to a ByteArray, convertToBytes:

[as3]
public static function convertToBytes(m : Object) : ByteArray {
var ba : ByteArray = new ByteArray();
ba.writeObject(m);

return ba;
}
[/as3]

The ByteArray class has lots of useful writeXXX methods depending on what you’re doing. In this case we use writeObject() which serializes an object to AMF, and writes it to the ByteArray.

Second, I built a method that compares two ByteArrays. There are several ways that this can be accomplished, I chose straight iteration. If someone knows a faster way, please let me know. The faster the better. Here it is:

[as3]
public static function isByteArrayDifferent(base : ByteArray, revision : ByteArray) : Boolean {
if (base.length != revision.length)
return true;
for (var i : int = 0; i < base.length; i++) {
if (base[i] != revision[i])
return true;
}
return false;
}
[/as3]

Obviously, if they are different lengths they’re not the same. I like that the method short circuits if a difference is detected, which will be the most common case.

Finally, I wrote the method that accepts the two objects, and returns a Boolean indicating whether a change has been detected. It’s simple, as the preceding two methods do all of the work.

[as3]
public static function hasObjectChanged(a1 : Object, a2 : Object) : Boolean {
return isByteArrayDifferent( convertToBytes(a1), convertToBytes(a2) );
}
[/as3]

And it works pretty well. There are a few gotchas I will point out, fortunately none of them apply to my application specifically.

First, suppose your object has a collection with two objects, 1 and 2. The order matters when the objects are serialized. An object with 1,2 will serialize differently than the same object with 2,1. Depending on your application you may have to handle this case specially (if 2,1 is considered the same as 1,2).

Second, you can’t use this method to tell if two objects are the same instance in memory. There are other ways of doing that, this isn’t one of them.

ByteArrays are very useful and have loads of other applications (see the ObjectUtil.copy() method to see how to make copies of objects using ByteArrays. I’ll include a quick method I threw together to output the contents of a ByteArray (it’s very similar to isByteArrayDifferent(), in case anyone’s interested. I used it for debugging, but it can be easily modified to return the String instead of outputting it.

[as3]
public static function outputBytes(label : String, block : ByteArray) : void {
var s : String = "";
for (var i : int = 0; i < block.length; i++) {
s = s + uint(block[i]).toString(16) + " ";
}
trace (label + ": " + s);
trace();
}
[/as3]

Tags: , , ,
No comments yet.

Leave a Comment

*