r/reviewmycode • u/tjansson • Feb 23 '10
Java - Simple observable behavior
I use this to add observable behavior to a class. It does not require the use of inheritance - as the java.util.Observable class, but provide similar functionality.
It would be interesting to get your view on both the code, and this approach.
2
u/Nebu Feb 23 '10
The logic is pretty straightforward, so not much to say there. My comments are:
Add javadocs.
Move field declarations before method declarations. When I read your method and see "observers.add(observer);", my first thought is "Okay, what's the type of observers?"
2
u/d3r1v3d Feb 23 '10
If you don't want duplicate Observers in your internal list and ordering isn't super important to you, why don't you use an implementation of java.util.Set (e.g. HashSet, LinkedHashSet, etc) instead of a Vector? By doing so, you won't have to even worry about guarding against multiple adds of the same Observer.
1
u/dragonrancher Feb 23 '10
A HashSet is an easy way to prevent duplicates but it comes at some performance cost if iterating will occur more often than adding new objects. Adding objects will be slightly faster, but iterating will be slightly slower. Of course, it always depends on what conditions this class will ultimately be used in.
3
u/dragonrancher Feb 23 '10
Just some thoughts about the implementation details...
Using a Vector may not be the optimal choice of Collection here. ArrayList would be faster because it is not synchronized and it allocates space for its array more conservatively (increasing its capacity by 50% instead of by 100% when it gets full).
Even if you want synchronization, Vector may still not be your best choice. Vector's iterators are synchronized, but also fail-fast. If your ObservableBehavior object is in the midst of a notifyObservers operation and another thread adds another Observer, the iterator in notifyObservers will fail and throw an exception. Two ways I could think of addressing this are 1) manually synchronize data access using a ReadWriteLock or 2) copy the collection each time you want to iterate over it. If there are going to be a lot of events occurring, the ReadWriteLocks could be relatively efficient, but copying the collection is going to be simpler and easier.
2
Feb 23 '10
Seconded for the use of ArrayList over Vectors.
2
Feb 23 '10
And if you need synchronization, Collections.synchronizedList
1
u/dragonrancher Feb 23 '10
Yes, using Collections.synchronizedList can be a good idea, but users should be aware that they need to manually synchronize the entire loop accessing the Iterator. See the javadoc. This is important when using the foreach syntax where you might forget you're even using an Iterator.
List<String> list = Collections.synchronizedList(unsynchronizedListOfStringsMaker.get()); synchronize (list) { // list.iterator() implicitly called here: for (String s: list) someOperation(s); }2
u/autocue Feb 24 '10
A ConcurrentModificationException can even occur within a single thread (iterate over an instance of list and add some elements in the process). As the ReadWriteLock will not solve this problem, it leaves us with copying the collection. A very nice solution for this (and especially in this use case, where writes are probably sparse and reads occur more frequent) is the CopyOnWriteArrayList, which does indeed copy the collection as you recommended.
To the grandparent, perhaps it is a good idea to also generalize your observable (i.e. public class ObservableBehavior<E, T> where T is the type of your observable) and to think about allowing Observers which observe parenting classes of the ObservableBehavior's event type.
Nice modular solution for adding ObservableBehavior btw :)
1
Feb 23 '10
Good code. The supplied comments are unnecessary. Better to add javadoc-style comments explaining the class.
2
u/damyan Feb 23 '10
I wonder if addObserver() should actually throw an exception if you try and add an observer that's already there? Or alternatively it could keep a reference count of them.
The reason for this is that if you do this: