| «« My Brief History With Technology | CD Run »» |
|
About
I'm Ryan Lowe, a Software Engineering graduate living in Ottawa, Canada. I like agile software development and Ruby on Rails.
I write this blog in Canadian English and don't use a spell checker. Typos happen.
Projects
» Full-time Ruby on Rails freelancer
» Full-time with Rails since May 2005 » Former committer for RadRails (now Aptana) » I also have a few Rails side-projects in development: 1. wheretogoinTO.com Toronto nightlife 2. Hey Heads Up! TODO list and sharing 3. Layered Genealogy family history research 4. foos for foosball scoring 5. fanconcert for music fans (on hold) Hiring Rails developers? I can telecommute by the hour from Ottawa, Canada »» Email: rails AT ryanlowe DOT ca
BulletBlog
Now hosted on Hey! Heads Up -- check it out!
Syndication
Pings
Recent
Derek Lowe's (Ryan's older brother) words at Ryan's funeral
blog@ryanlowe.ca no more Forging Email Headers: Good, Bad or Ugly? Sarcastic Dictionary (Part 1 of Many) Tags Hierarchies Twisting Rails is Risky Business Risky Business? My Take on Early Alphas Whoa, it's August 2007 Closing Comments A Postscript to "Growth at the grassroots" »» All Blog Posts
Linkage
del.icio.us/ryanlowe
technorati/ryanlowe.ca/blog Aurora Roy Jim Andrew Trasker Travis Kibbee Karen Dr. Unk Ayana Van Bloggers Joel Spolsky Robert Scoble Tim Bray Dave Winer Raymond Chen James Robertson Ruby/Rails Bloggers rubyonrails.org weblog David Heinemeier Hansson Dave Thomas James Duncan Davidson Mike Clark Jamis Buck Signal vs. Noise Tobias Luetke Amy Hoy: (24)slash7 Jeremy Voorhis Eclipse Bloggers Planet Eclipse EclipseZone Luis de la Rosa Eclipse Foundation Kim Horne Billy Biggs Ian Skerrett Mike Milinkovich Bjorn Freeman-Benson Denis Roy
Archives
|
Making a Listenable Model Out of Vector
To implement the model-view-controller (MVC) design pattern for AudioMan I need a model. The Vector class is good enough but I also need to be able to attach listeners to a Vector so that the user interface (the TableViewer's content provider) is notified when the model changes. To do that I needed to subclass Vector and add the notification code. The problem with subclassing Vector is the number of mutator methods it has: well over a dozen. Mutator methods change the state of an object. So if I have listeners attached to my subclass of Vector, every time one of those mutator methods is used I have to notify the listeners. Overriding all of those mutator methods would be a big PITA. So I chose three methods: add(Object), remove(Object) and clear(). I would only use these three methods to mutate my new class VectorModel, a subclass of Vector. I override every other mutator method and make it throw UnsupportedOperationException. I got this idea from the Collections class, which contains handy static methods for manipulating Collections. In it there are a bunch of methods that return unmodifiable (read only) versions of Collections. For example: public static List unmodifiableList(List list) takes a List as input and returns an unmodifiable version of that List. They do it by wrapping the List inside of another List that has all of the mutators throwing UnsupportedOperationException. To complete VectorModel I need the code to add and remove listeners and to notify the models. Java has a handy class for managing a list of listeners called EventListenerList. private EventListenerList listenerList; I wrote public addListener and removeListener methods like: public void addListener(ModelListener listener) public void removeListener(ModelListener listener) though you may want to do more parameter checking (I've removed it for simplicity). You may have noticed a new class in the above code: ModelListener. It's an interface that extends the EventListener interface, used for event handling. In that interface you write abstract method headers for the events you want to notify the listeners about. These usually correllate with the mutator methods. For example, mine were: public interface ModelListener extends EventListener When you mutate the VectorModel you notify the listeners by iterating through the list of added listeners, like so: EventListener[] listeners = listenerList.getListeners(ModelListener.class); for (int i = 0; i < listeners.length; i++) The classes you want listening in turn have to implement the ModelListener interface and write some code for the three methods. In my case that was JFace's TableViewer content provider. The TableViewer class has methods to add and remove objects so this was fairly simple. The last piece of the puzzle is the ModelEvent class, which gets sent with the event to the listener. When I notify the user interface that some object has been added I want to send that object, so that the user interface knows what to add. The ModelEvent constructor takes the object in question as a parameter. public ModelEvent(Object source, Object item) The listener then extracts the added or removed object from the immutable ModelEvent object by using the getItem() accessor method. So that's how I'm doing my model. Any questions or suggestions? Update 10:12 PM Yeah, it's not quite that simple. The Vector class has a lot of methods that do the same thing, left over from the old days when it didn't implement the List interface. To simplify their lives the implementors of Vector just used other methods sometimes. So I can't use remove(Object) if I override removeElementAt(int) and make it throw UnsupportedOperationException because remove(Object) uses that method. I had to go right to the top and see which methods didn't call any other methods. Those ones turn out to be: public synchronized void addElement(Object o) So why do I have two remove...() methods? Well I need to be able to remove objects by reference, but I don't want to have to call indexOf(Object) every time. The other method, removeElementAt(int) actually does the removal and is called by removeElement(Object). So now I have to test it too. Oh joy. Posted at February 02, 2004 at 03:35 PM ESTLast updated February 02, 2004 at 03:35 PM EST Comments
Awesome. A small thing that I do with list iteration (pardon if some of this is .netish..... they all look the same when no compiler is involved).. instead of.. for (int i=0; i< myList.Count; i++) i use for (IEnumerator allElements = myList.GetEnumerator(); allElements.MoveNext();) (and if foreach is avaialble that works too)... The main reason is that my enumerator variables scope is now even more restricted - so that if I need to iterate over 2 lists I can use the same variable name without possibly introducing bugs because of scope issues (aka using the wrong variable if you created two similarly named variables. » Posted by: aforward at February 2, 2004 11:32 PMI use arrays because they are copies I'm looking at. I don't have to worry about it being threadsafe or anything. » Posted by: Ryan at February 2, 2004 11:40 PMAha, I have never really used arrays... maybe I should start. » Posted by: aforward at February 2, 2004 11:54 PM |