commons-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Stephen Colebourne" <scolebou...@btopenworld.com>
Subject Re: [collections] NotifyingCollections - capturing rich non-uniform data
Date Thu, 14 Aug 2003 21:16:49 GMT
Been busy with [lang]....anyway

There are various issues with designing an event system that bering the
three approaches into conflict:

1) Listener methods -
[neil] One Listener method, different event types
[michael] Many Listener methods, one event type
[stephen] Favour one method, but....
What if the user doesn't want a listener?

2) Event classes -
[neil] Very simple + Detail in OO hierarchy
[michael] Standard, as detailed as possible
[stephen] Standard, as detailed as possible, but....
What if the user wants full detail to allow for undo?

3) OO/Patterns -
[neil] Follow OO guidelines, type hierarchies, factories, etc.
[michael] Simple solution to get job done
[stephen] Basic solution, not much OO, but...
What if the user want a more OO solution?

Now you may think that #3 is a repeat of #2, but I added it as it is clear
that there is a difference in programming approach between Neil and myself.
I am not greatly in love with OO. It has its moments of convenience, but I
do not really buy into it. (Many commons committers are like this, as
commons libraries tend not to be terribly fancy OO designs). Thus #3 is
included because I want to note that if I commit a final solution, it won't
necessariily be overly OO, just practical.

#1 is a straight choice, in theory. #2 is a choice too, simple or detailed.
Except that too much constriction is placed by the NotifyingCollections
design.

What if I want to have events recieved without writing a Listener
implementation and registering it. Can I? What if I want events that tell
you as much as possible, but not enough to support undo?

The design I posted before
http://www.scolebourne.eurobell.co.uk/Observed2.zip delegates ALL aspects of
the event handling to another object. That object MIGHT be the standard one
that calls Listeners. OR it might be one that logs calls directly. OR it
might call a method on another object directly (no listener). This is much
more flexible.

The same handler also enables the factory behaviour from
NotifyingCollections without being so explicit. The factory from
NotifyingCollections limits the arguments that can be passed into the event
object. The ObservedCollection handler creates the event object itself, so
can do what it likes, allowing some very flexible possibilities. (eg.
creating a handler that is linked to multiple lists and updates one based on
changes to another while applying filtering....)

The default ObservedCollection event stores as much data as possible for
List, Collection (Map/Set/Bag can be added). It does not attempt to store
absolute data about the change, so an undo facility is not possible. In
other words, the single event class is not trying to compete with the
hierarchy like for like. Is this anti-OO. Yes, I expect so. But so is the
Collections API in that it has UnsupportedOperationException. Typecasting is
a pain however you try and look at it.

The conflict between the rich hierarchy [neil] and the 'rich as possible
monolithic' [stephen/michael] is most acute in defining which is the base
event class for the listener. The solution is to not have the conflict -
ObservedCollections allows one handler for monolithic that specifies one
Listener that is passed a monolithic event, whereas for rich events a
separate handler, with a separate Listener that is passed the rich event
abstract base event type can be used. Two handlers, two listeners, one for
each problem solution.


The biggest problem with all this is that the collection returned by
getSource() on the event. If the observed collection is wrapped (eg. by a
SyncronizedCollection) then getSource() SHOULD return the wrapped
collection, but it can't as it doesn't know about it. This problem applies
to all designs so far.

Stephen

----- Original Message -----
From: "Neil O'Toole" <neilotoole@users.sourceforge.net>
> [stephen] > My goals for [collections] is to add something small and
> simple that doesn't prevent a user adding something more complex.
>
> [michael] > I think I would prefer multiple listener interfaces and a
> simple event to a single listener interface and either a hierarchy of
> event classes or a typed-using-masks event.
>
> Obviously one of the concerns for the notifying package is that it be
> small and easy to use, yet we also want it be flexible and powerful
> (when required). This is why NotifyingCollections uses a pluggable
> event system. And the simplest usage for NotifyingCollections is indeed
> small and simple. The user need only know about one decorator (e.g.
> NotifyingList), one listener (CollectionListener), and one event
> (ModifyingEvent). The default 'simple' event package in
> NotifyingCollections is just that, simple. The package can be used as
> easily as this:
>
>  NotifyingCollection nc =
>   NotifyingCollectionsUtils.notifyingCollection( c );
>
>  nc.addCollectionListener( myCollectionListener );
>
>  // do something
>  nc.add( "hello world" )!
>
>  ModifyingEvent event = myCollectionListener.getEvent();
>  assertTrue( event.getPreEventSize() == event.getPostEventSize() -1);
>
> The 'simple' package has one event class, SimpleCollectionEvent. The
> user does not even need to know of this class as it is simply a minimal
> implementation of ModifyingEvent (which is abstract). The only data
> provided is the size of the collection before and after the event. It
> does not contain references to the arguments that caused the event to
> be fired or references to the items added/removed etc. from the
> collection by the event. That would be considered rich data.
>
> The 'rich' event package has an event hierarchy, which (I hope)
> captures the semantics of collection manipulation quite well. There is
> an AddEvent, a RemoveEvent, a ListAddEvent, a MapPutEvent... a total of
> 9 events. I believe this is close to the minimum number of classes
> necessary to richly model collection manipulation in a clean OO manner.
> (Though if anyone can do better, they can just plug in their own event
> package!).
>
> The third event package, 'monolithic', is an attempt to capture rich
> data (like the 'rich' package) in a single event class, i.e. the design
> idea that has been floated on the list. While a single event class
> works fine for SimpleCollectionEvent, it does not work well with rich
> data.
>
> Let's take three collection manipulations, for which we want to gather
> rich data:
>
> a) remove several items from a list (e.g. #removeAll)
> b) set a list element
> c) put an entry into a map
>
> What data do we need to capture?
>
> a) The removed items, and the indices of each removed item (note that
> they are not necessarily in sequence)
> b) The value of the list element, the index, and the previous value at
> that index
> c) The key, the value associated with that key, and the previous value
> associated with the key (if any)
>
> So, in this *one* monolithic rich event class, we need a method:
>
> 1) to access added/removed/set items (e.g. #getItems: Collection)
> 2) to get indices (e.g. #getIndices: int[])
> 3) to test if there was a former value associated with an index/key
> (e.g. #hasPreviousValue: boolean)
> 4) to get previous value if any (e.g. #getPreviousValue: Object)
> 5) to get key associated with a Map put event (e.g. #getKey: Object -
> though this could be possibly be shared with #getIndices)
>
> Those who already see what's wrong with this can skip this section ;)
> For the unconvinced, let's say we add an item to a plain ol'
> collection:
>
>  nc.add( "hello" );
>  MonolithicEvent event = myListener.getEvent(); // grab the event
>                                          // from a listener somewhere
>
> This monolithic event will have a method #getIndices, which has no
> meaning in the context of a collection. Presumably #getIndices should
> return null in this case? (as a zero-length indices array would violate
> the invariant that the number of indices should be the same as the
> number of added/removed items). Not pretty. There are a whole bunch of
> similar methods in MonolithicEvent that are specific to the specialized
> types in collections, and don't belong in a general type. This is a
> textbook example of when to use subclassing.
>
> I don't argue that the included 'rich' event package is the only or the
> best possible model of collections manipulation. Indeed I completely
> redesigned the package at least once. What I do argue is that this
> shows a) the need for a flexible event system and b) the difficulties
> with trying to capture rich heterogeneous data in a single class.
>
> - Neil
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: commons-dev-help@jakarta.apache.org
>


Mime
View raw message