Monday, November 06, 2006

A Focus on Data Structures

HEAD hasn't had many changes lately for data binding because of the 3.3M3 release. But this doesn't mean that nothing has been happening. Changes have been occurring in the concurrency branch (Bug116920_investigation) for, you guessed it, concurrency. But the branch has also been used for removing provisional packages and the refactoring of some of the provisional code into API. But before I get into the other changes it's very important to us that we get as many eyes on the concurrency approach as possible. If you're a concurrency guru or just someone with a interest please read through bug 116920 and provide feedback if you have any.

The other changes that have been occurring are interesting. If you are vaguely familiar with JFace Data Binding you probably know that data binding is comprised of two parts: observables and bindings. The idea behind bindings is pretty straight forward and what you would expect from the name. They bind two entities and provides a MVC-esque flow of data to keep the 2 in sync. This is normally applied in the context of a graphical user interface in order to keep the UI in sync with a model. But there's more that needs to be implemented in order to make this synchronization and this is where observables come in. Observables are an implementation of the observer pattern and they create a common abstraction that allows for the observing of changes in an object. This all begins with IObservable but branches out into:

IObservableValue

Interface for the observing of a single value.

IObservableList

Interface for observing changes in a List. The notifications are notifications of when items are added or removed from the List. If an object being maintained in the List changes events are not fired from the IObservableList implementation.

IObservableSet

Interface for observing changes in a Set. Like IObservableList the change events are for items being added and removed from the Set.

IObservableMap

Interface for observing changes in a Map. And you guess it, same type of behavior as the previous interfaces but for a Map.


For all of the above interfaces there are default mutable, or writable, implementations.

  • WritableValue

  • WritableList

  • WritableSet

  • WritableMap


By providing these implementations we, and any consumer, can use the observable implementations for any need that arises. The need doesn't have to be for binding a widget to a model, it can be any use case that you're wanting to provide notifications for a data structure. The idea is that the abstraction should feel common and also provide building blocks, just like the Collections Framework, for application needs.

In order to drive home the idea that the core of data binding doesn't necessarily have anything to do with UI the core interfaces and implementations that are UI unaware will reside in plug-in org.eclipse.core.databinding. The project is currently named org.eclipse.jface.databinding but will soon be renamed, see bug 153630 for details.

4 comments:

Jesse said...

Hi Brad

I'm Jesse from the Glazed Lists project and I would love for you guys to check out our project for implementing IObservableList, plus some other stuff you should find necessary in this project.

Although implementing a simple IObservableList is really easy and can be done and tested within a day's work, implementing a powerful IObservableList takes a long time to get right. I'd like to convince you that this is worthwhile.

Some stuff to read:
http://publicobject.com/2006/06/fine-grained-events-for-performance.html

The whitepapers here:
http://publicobject.com/glazedlists/wiki/index.php?title=Team

I believe that to do observable lists right, you need:
- fine grained events
- list transformations
- listener dependency management
- a concurrency strategy

Brad Reynolds said...

Hi Jesse.

>"I'd like to convince you that this is worthwhile."
No need to spend time convincing us, we know. :) This project has been in the works for a couple of years now and I'm pretty sure Glazed List provided some inspiration.

On your points of what is necessary...
> "- fine grained events"
We provide those.

> "- list transformations"
Can you explain what you're referring to?

> "- listener dependency management"
I'll try to look through your documents and pass this on as well to the team. I'll try to comment on this later in the week when I have some time to go over it.

> "- a concurrency strategy"
That's what bug 116920 is about. We're still working through the strategy so feel free to comment on the bug.

If you have any specific concerns about the current implementation feel free to comment here or log something in bugzilla. We'd love to have your input.

Jesse said...

"List transformations" is our word for decorated views of observable lists.

Your standard transformations are sorting and filtering. But more interesting transformations are possible:
- element functions, such as turning a list of songs into a list of artist names
- duplicate removing, such as turning a list of artist names into a list of artist names with no duplicates
- composition, turning N observable lists into one observable list
- range filtering - show the first N elements, the last N elements, etc.

Dependency management. A simple example: in the Glazed Lists issues browser, the users list acts as a filter for the issues list. Both lists are backed by different series of list transformations to a common source. For events to flow properly, the users list must be updated before the issues list, so that filtering changes aren't made a dependency is in an inconsistent state. Glazed Lists manages notification order across a chain of listeners, which we call a pipeline.
See http://www.martinfowler.com/eaaDev/OrganizingPresentations.html#observer-gotchas

Brad Reynolds said...

Hi Jesse, thanks again for the comments. I apologize that I've been too busy to respond until now.

JFace provides filtering and sorting. It's a different approach than what you speak of but it keeps the logic in the UI rather than in the data structure. This allows us to use the same structure for multiple views and I think it also protects us against some of the issues encountered when having lists observe lists that observe lists. It doesn't make us invulnerable but I think it makes things simpler. It also keeps it data structure agnostic. It doesn't rely upon JFace Data Binding or even the Java Collections Framework. All we need to provide is the glue to bind the two together.

But there are classes today that provide transformations, UnionSet is an example of this. The nice thing is that it knows of it's dependencies so it doesn't need to rely on an external event aggregator, it is the aggregator.

But in saying that we might have the need for such concepts in the future. We're open to them but we need to have use cases first.

It looks like you've simplified your list use case. I'm curious how a user list and issues list are backed by a common source. I've used event aggregators before but I tend to prefer a simpler approach if one is available. I think the most important thing stated in Martin Fowler's article is to not make lists that observe lists that observe lists. My main reason for wanting to avoid this is that it's difficult to debug and is somewhat error prone. The simpler we can keep it the better. But again, if the use cases arises we'll look into adding support.

-brad