Many people have asked "How does the Barracuda
approach differ from the traditional DiscRack example?". This is the answer I gave to
Dan Rosner recently when he asked this question...Hi Dan!
> This looks interesting, since I've worked with the old
> DiscRack example included in the EE4 alpha. For the benefit of the
> less experienced among us, could you give a "30,000 meter" overview
> of how the methodology you propose differs from that integrated in the
> DiscRack example?
This is actually a good comparison...if you look at Login.java in the DiscRack example
(I'm using Enhydra 3.0.3, but I think it's close) it corrolates pretty well with the
EventHandler example because functionally they're doing very similar things. So how are
they different?
First of all, the discRack stuff extends from BasePO. BasePO uses what I call a
psuedo-event methodology. If the URL contains an event=XXX parameter the BasePO tries to
invoke handleXXX(), if not, it calls handleDefault(). On the plus side, this approach is
pretty easy to understand. On the down side, it uses reflection (which is slow), but it
also forces you into a 1:1 relationship between events and handlers...this works fine for
simple apps, but gets difficult as the screen complexity because you end up with huge
handlers which are not very reusable or componetized.
The Barracuda model converts incoming requests into actual events. Any number of
handlers can listen for events (just like in a stated app), so if I need to do multiple
things in response to a request, I just create multiple listeners. Each one can do one
thing (and do it well). This increases reusability through high cohesion.
The Barracuda model also uses a built in Model 2 dispatching pattern. This allows
listeners (Controllers) to handle the incoming request and then specify the appropriate
view to be rendered by firing another event (whoever handles that corresponds to the
View).
Not only does this provide further decoupling, it also provides a secure API -- people
cannot just invoke any event handler in an app...they can only invoke request events,
whose handlers in turn can determine whether or not they are authorized to do whatever
they are requesting and route them on accordingly. Now granted, this is slightly more
complex (two steps instead of one), however, it seems to be quite a bit more scalable and
reusable.
Now let's look at some really interesting features of the Barracuda event model
approach. HttpRequestEvents (the only thing served through an HTTP gateway) implement a
marker interface called Polymorphic. When a polymorphic event is dispatched, all of it's
parent events are dispatched first (because it _is an instance_ of those events).
For instance, in the Barracuda example, we have an event hierarchy that looks something
like this:
- DefaultBaseEvent
- HttpRequestEvent (implements Polymorphic)
- ...
- LoginScreenEvent
- GetLoginScreenEvent
- AttemptLoginEvent
- MainScreenEvent
- HttpResponseEvent (implements Exceptional)
- ...
- RenderEvent
- RenderLoginScreenEvent
- RenderMainScreenEvent
- ErrorEvent
When the event dispatcher goes to dispatch the GetLoginScreenEvent, it first dispatches
parent events: HttpRequestEvent, and LoginScreenEvent. This allows us to install
"global" listeners on entire portions of the system.
For instance, in the sample app, whenever someone fires an event that is NOT an
instance of LoginScreenEvent, we want to first make sure they're already validated. If
someone fires some kind of LoginScreenEvent and they are already validated, I can easily
redirect them to Main screen by simply marking the event queue as handled and firing a new
GetMainScreenEvent.
The key thing to note here is that the individual handlers only need to know about
their particular domain (ie. getting the actual screen, attempting to login in, etc). They
do not need to know about the special rules that tie everything all together. In typical
approaches (like the BasePO) all this logic is tightly coupled in one place. With an event
driven approach it can easily be decoupled (and hence more easily reused).
Another feature of the event dispatching in Barracuda is what we call Exceptional
behavior. When an event implements the Exceptional interface (notice all
HttpResponseEvents do so), if that event is not handled the dispatcher automatically fires
the parent event, (which propogates up the chain until someone handles it, just like with
Exceptions).
This is _very_ useful with error handling. Let's say you want to install a default
error page in a system. Just add a listener on ErrorEvent, and no matter how many error
classes get added to the system, you are always guaranteed to catch it. And if you want to
add more specialized error messages for a particular type of error, you just create a
specific Error Event for that situation and install a listener for that particular event.
It is very flexible.
Here's another example. Let's say you want to add some kind of logging for just _part_
of the system, perhaps screens 1,2, and 3, but NOT 4. It's very easy to modify the event
hierarchy so that screen events 1,2 and 3 all extend from a common event class. Add a
listener for that type of event and your in business. Again, this facilitates loose
coupling and reuse. And it doesn't have any impact on existing event listeners.
Perhaps the most valuable aspect of the Barracuda event oriented approach is that it
sets us up for creating servers side components which could be bound to portions of an
XMLC class. Want to set data in a complex XMLC list or table? Just grab the object's Model
and program against it (just like in Swing). Want to be notified when someone presses a
button? Just add an event listener for that particular component. When the action is
invoked on the client side it gets routed back to your handler code automatically. This is
where this stuff is going...
The biggest drawback to event oriented programming is you have to think a little harder
about how you want to organize your system hierarchically. I tend to start by identifying
the basic screens in the system. For each screen, what are the possible requests?
responses? errors? How do I organize these hierarchically?
Once I have the event hierarchy defined, then it's fairly easy to start adding event
handlers for "leaf" events (those at the end of the hierarchy). Then I go back
and add handlers for events further up in the hierarchy, establishing the system flow.
Once you get the basic idea, it's actually very straightforward. It allows you to create a
working system very early on and then gradually flesh out the functionality. It's easy to
integrate with XMLC, and it performs very efficiently.
One of the things I'd like to do fairly soon is create a "NewBarracudaApp"
utility/wizard to streamline this whole thing and automate a lot of the mundane aspects.
This would make it really easy to sit down and create a working Barracuda app in no time. |