1. Po raz pierwszy odwiedzasz EDU. LEARN

    Odwiedzasz EDU.LEARN

    Najlepszym sposobem na naukę języka jest jego używanie. W EDU.LEARN znajdziesz interesujące teksty i videa, które dadzą Ci taką właśnie możliwość. Nie przejmuj się - nasze filmiki mają napisy, dzięki którym lepiej je zrozumiesz. Dodatkowo, po kliknięciu na każde słówko, otrzymasz jego tłumaczenie oraz prawidłową wymowę.

    Nie, dziękuję
  2. Mini lekcje

    Podczas nauki języka bardzo ważny jest kontekst. Zdjęcia, przykłady użycia, dialogi, nagrania dźwiękowe - wszystko to pomaga Ci zrozumieć i zapamiętać nowe słowa i wyrażenia. Dlatego stworzyliśmy Mini lekcje. Są to krótkie lekcje, zawierające kontekstowe slajdy, które zwiększą efektywność Twojej nauki. Są cztery typy Mini lekcji - Gramatyka, Dialogi, Słówka i Obrazki.

    Dalej
  3. Wideo

    Ćwicz język obcy oglądając ciekawe filmiki. Wybierz temat, który Cię interesuje oraz poziom trudności, a następnie kliknij na filmik. Nie martw się, obok każdego z nich są napisy. A może wcale nie będą Ci one potrzebne? Spróbuj!

    Dalej
  4. Teksty

    Czytaj ciekawe artykuły, z których nauczysz się nowych słówek i dowiesz więcej o rzeczach, które Cię interesują. Podobnie jak z filmikami, możesz wybrać temat oraz poziom trudności, a następnie kliknąć na wybrany artykuł. Nasz interaktywny słownik pomoże Ci zrozumieć nawet trudne teksty, a kontekst ułatwi zapamiętanie słówek. Dodatkowo, każdy artykuł może być przeczytany na głos przez wirtualnego lektora, dzięki czemu ćwiczysz słuchanie i wymowę!

    Dalej
  5. Słowa

    Tutaj możesz znaleźć swoją listę "Moje słówka", czyli funkcję wyszukiwania słówek - a wkrótce także słownik tematyczny. Do listy "Moje słówka" możesz dodawać słowa z sekcji Videa i Teksty. Każde z słówek dodanych do listy możesz powtórzyć później w jednym z naszych ćwiczeń. Dodatkowo, zawsze możesz iść do swojej listy i sprawdzić znaczenie, wymowę oraz użycie słówka w zdaniu. Użyj naszej wyszukiwarki słówek w części "Słownictwo", aby znaleźć słowa w naszej bazie.

    Dalej
  6. Lista tekstów

    Ta lista tekstów pojawia się po kliknięciu na "Teksty". Wybierz poziom trudności oraz temat, a następnie artykuł, który Cię interesuje. Kiedy już zostaniesz do niego przekierowany, kliknij na "Play", jeśli chcesz, aby został on odczytany przez wirtualnego lektora. W ten sposób ćwiczysz umiejętność słuchania. Niektóre z tekstów są szczególnie interesujące - mają one odznakę w prawym górnym rogu. Koniecznie je przeczytaj!

    Dalej
  7. Lista Video

    Ta lista filmików pojawia się po kliknięciu na "Video". Podobnie jak w przypadku Tekstów, najpierw wybierz temat, który Cię interesuje oraz poziom trudności, a następnie kliknij na wybrane video. Te z odznaką w prawym górnym rogu są szczególnie interesujące - nie przegap ich!

    Dalej
  8. Dziękujemy za skorzystanie z przewodnika!

    Teraz już znasz wszystkie funkcje EDU.LEARN! Przygotowaliśmy do Ciebie wiele artykułów, filmików oraz mini lekcji - na pewno znajdziesz coś, co Cię zainteresuje!

    Teraz zapraszamy Cię do zarejestrowania się i odkrycia wszystkich możliwości portalu.

    Dziękuję, wrócę później
  9. Lista Pomocy

    Potrzebujesz z czymś pomocy? Sprawdź naszą listę poniżej:
    Nie, dziękuję

Już 62 619 użytkowników uczy się języków obcych z Edustation.

Możesz zarejestrować się już dziś i odebrać bonus w postaci 10 monet.

Jeżeli chcesz się dowiedzieć więcej o naszym portalu - kliknij tutaj

Jeszcze nie teraz

lub

Poziom:

Wszystkie

Nie masz konta?

Google I/O 2009 - Best Practices for Architecting GWT App


Poziom:

Temat: Media

Ryan: Hello. My name is Ray.
And I'll be talking to you today
about GWT application architecture best practices.
We tried to find a drier title, but this is the one that won.
I am stunned, amazed, and flattered
that you all are here when you could be eating lunch
or learning about Wave,
so thank you very much for showing up.
I was going to go get lunch myself if you hadn't.
So, I'll warn you also
that all of my rehearsals of this have run long.
And I've responded to that by adding more slides
to the presentation, so I'm probably going to do
a lot of mumbling and a lot of talking fast.
And we'll see where it goes.
So what are we talking about today?
This is about how to put together
a nontrivial GWT application.
Something that you actually want to ship.
Something that you're going to have
more than two or three people at a time working on
without stomping on each other's toads.
Toes.
We're going to look, in particular,
at the client side,
because that's where I tend to pay attention.
And what we're really exploring here
are lessons that were learned over the past year
building the new AdWords user interface.
AdWords is a small product at Google.
You may have heard of it.
And it's recently been-- the user interface of that
has been revamped from the ground up
as a GWT application.
But first, let's go
to the last slide of the presentation.
If you take nothing else away today,
there are three things that you really want to remember.
First thing, which I'm not going to talk about much at all,
but still, you want to remember it,
get your browser history right, and get it right early.
One of the first things that you should nail down
on your app if you want it to work
is what the back button and the forth button
are going to do to your users,
how they're going to be bookmarking things.
GWT gives you a very nice history library
to take care of that,
and if you apply it early, it's easy.
If you apply it after the fact,
and try to retrofit it in, not so easy.
The next takeaway point is you're going to use
an Event Bus as the backbone of your application
to let yourself-- let you make sure
that the left hand of your app
has no more than a nodding acquaintance
with the right hand of your app.
Preferably through conversations
brokered by a neutral third party.
And then finally, you're going to start saying to yourself,
"DI + MVP FTW."
Which is to say, dependency injection
and the Model/View/Presenter pattern for the win.
And I promise that that'll all make a lot more sense
in about 50 minutes or so.
So let us start with a demonstration
of the app that I mentioned, the AdWords UI.
Which I, of course, forgot to cue up.
So give me a moment to rectify that situation.
Yeah.
We might just blow off this part of it
if I can't get my act together here.
There it is.
And it has decided to reload.
Good.
So this is live right now.
I'm looking at the production site.
If I run into glitches here, there are people
in ten countries around the world,
a number of users--
which number I have been cautioned not to disclose--
but they'd notice.
You can see that this is a pretty traditional kind of
table full of data sort of app.
But we've got nice little widgets
like the GWT Incubator tree view, fast tree view,
helping us guide our way through things here.
Our users are going to do things using the paging scrolling table
from the GWT Incubator to make lots of edits of their data.
Maybe several fields at once in that kind of a thing.
Or I might want to type around and change my matching
and change my CPC.
I forget what that means, actually,
although this is my live account.
So I'm not going to actually commit any of these changes
that I'm making.
They might take advantage of the GWT visualization stuff
that you might have learned about yesterday
to show graphs and such.
There's my pretty graph.
I'm not doing so well, really.
Maybe we're going to compare my click-through rate
to the number of clicks that I get
and see if there's a correlation there.
Yep, there is.
So this is the app that we've built
with a team of more than a few engineers.
I really forget the handful, but the sizeable handful
has grown lately.
And we're all able to commit changes to this
and test portions of this
without necessarily talking to each other every day
and destroying each other's code.
So let's get back to the presentation.
And before we get back to the presentation,
let's get my speaker notes back,
or this is going to be a much shorter presentation.
Dude, it all worked. All right.
So we've got three major themes
that we're going to talk about here
on your way to making no bad apps the AdWords way.
You're going to embrace
the asynchronous nature of your app.
You are an AJAX developer.
And the first word in AJAX is "asynchronous."
Let's just not kid ourselves.
You want to always be decoupling your app.
Like I said, the bit over here and the bit over here
just shouldn't really know much about each other.
And finally, you're going to strive
to achieve statelessness,
so as various bits of your infrastructure fall over,
the other bits aren't bothered by it.
So, embracing asynchrony.
These Keynote builds, man. I can't get enough of 'em.
Yes.
The "A" in AJAX is for asynchronous.
There is nothing interesting that you're going to do
in your application that isn't going to,
at some point or another, require some kind
of an asynchronous callback.
So you need to just come to terms with that fact.
You don't want to spend a lot of energy
trying to create little synchronous bits
that you can call once everything is swapped in,
because you've got better things to do with your time.
And if you do it well,
it's really not such a bad thing.
So here's an example
of what you might start out trying to do.
Through the talk, I'm going to assume
that we're writing an address book application.
A contact manager.
Could you all turn your cell phone ringers on, please?
The first thing you're going to do as a good developer
is you're going to create your data objects,
your model objects at the heart of your app.
And at the heart of an address book
we have contacts.
And my contacts have things like a name field,
and they have ContactDetails.
ContactDetails are an interface
to cover phone numbers and street addresses
and mother-in-law's birth date and that sort of a thing.
And the mistake that we made here
is that we are downloading the kilobytes and kilobytes
of ContactDetail information at the same time
that we're downloading our contact objects.
At least, that's what I see implied here.
That's a mistake.
The odds are that you want to get your hands
on that list of contacts with as little information
as you need as possible to display them to your users,
and then you want to move on from there
to fetch just the details that the user cares about.
So probably you want a structure that looks more like this.
My contact comes down with a list of DetailIds.
Some kind of opaque Id object.
It's probably a string under the hood.
But we're all Java developers here.
We like our strong types.
And that way we don't accidentally
ask for a contact with a phone number Id.
Another small change I made here,
which is kind of sneaky,
is that instead of using a list of ContactDetails,
doing the Josh Bloch thing and correctly providing
an interface for my field name
rather than the implementation class name.
I'm actually being very straightforward about the fact
that this is an ArrayList that I'm looking at.
I picked up this superstition from a talk
that I may have misunderstood on optimizing GWT RPC.
Where if you know that all you're ever going to send
across the wire are array list instances,
you don't want to give the GWT RPC--
the magic generator-- a chance to try to pick up
every other possible implementation of the list
and accidentally compile that into your app.
Small point.
So here we are, writing everything asynchronously.
And thinking to ourselves, "This is really going to suck."
It doesn't have to.
If we wrap our asynchronous calls
and asynchronous operations in the command pattern,
life can be pretty good.
A command pattern is a kind of a thing you do
where instead of having a call for this--
you know, a "do this" method and a "do that" method
and an "update the other" method--
we have objects that embody such calls.
So when it's time for me to get ContactDetails,
I'll make an instance of getContacts command,
and I'll hand that off to a command processor
and receive back in return a "got the contacts" response.
Every action I want to take with my app,
I encapsulate into an object like that.
And because I have all of these nice little command objects,
I've got a nice unit of currency
for implementing caching policies.
Maybe whenever I see the same GET request twice,
I'll cache away the response I got last time
and just return that to myself immediately.
Not bother with a server-side trip.
I've got a place where I can do batching mechanisms.
If I see five getCalls in succession
that aren't interlayered with updateCalls,
I'll have a timer going
where I can collect all of those guys
and put them into one do this group of operations command,
send that over to the wire,
over the wire in a single HTTP request,
rather than sending them off as bits of the app ask for them.
I got a place that I can centralize my failure handling.
So that every time I'm making one of these async calls,
I can inherit the "oops, something went wrong" behavior
rather than having to call it out in line there.
This also, if I want to get
a little bit forward-looking about this,
since I'm being honest with myself
about the asynchronous nature of what's going on here,
I've done all of the hard parts of chopping up my app
into bits that can be code split
at GWT.runAsync points.
I won't go into detail on GWT.runAsync,
because I'm sure you've been hearing about it for two days,
but do it this way and you're halfway there.
If you ever read a book on "how I'm going to implement
undo and redo on my application," the odds are
the term "command pattern" is going to come up.
And you're already working that way.
Because you're so clever, you're halfway there.
And then finally, the work that you've done
to isolate yourself from all of this RPC.
You've got a nice funnel point at which you could slip in
your HTML5 persistence type of code
when you decide you want to get into offline work.
So...
And it shines your shoes too.
So let's look at some actual code here,
how we might implement something like this.
Are there any AdWords engineers in the audience?
I can say anything. Okay.
[audience laughs]
They won't actually recognize this interface.
It's not exactly what they do.
But there are tools that they created
specifically for the project,
which when you pull back the hood,
this is what's going on there.
So I assume most of you are GWT developers
or you would be off looking at Wave.
You probably recognize the bottom half of this slide
where we are defining a GWT RPC service.
When we're working in this style,
we have a single service,
and that service provides a single call,
which is basically that "execute" word there.
Do something for me, and then return to me the response.
What happened?
And then the various somethings that we want our app to do
are going to implement this Action interface,
which we couldn't call Command because that word is taken
by another GWT class.
So an example of an Action object
that we might write would be this GetDetails thing.
Remember that we are working on our address book,
because the world doesn't have enough address books.
The command will be something as simple as
I'll instantiate the thing.
I'll give it a list of the Ids of the details that I want.
On the server, I'll do something clever
to actually fulfill that request
and send back across the wire a GetDetailsResponse object,
which has this nice ArrayList of the details
that I can then go and render.
Now, I'm not trying to say what the granularity
of your operation should be when you write your app.
I'm implying a pretty verbose world here
where I've got 500 different flavors of Get command,
based on my 500 different model objects.
Maybe you want to do something a little bit more generic
and just have a single Get something command
that's parameterized based on the type that you're fetching.
Maybe it's going to make sense for your app
to be more coarse-grained.
Maybe you want to write actions like,
"Get me all the stuff I need
to render the first page of the UI."
The level at which you break stuff down is up to you.
I'm just trying to pound home the idea
that you've got objects that embody
what it is you're trying to ask for
and the ways you ask for it.
And I took that digression one slide too early.
We've had our...whoops.
We have our action class.
We have our response class.
To make the code a little bit more pleasant to deal with,
we're going to write probably a third class
which is a custom subclass of the asynchronous callback
that happens--is handed to each GWT RPC call.
This gives us a spot to write failure handling
that the application code can inherit
and not have to worry its pretty little head about.
Maybe we'll pre-chew the response a little bit
to keep everybody from having to write
the Get, the Get, the Get, the code
to actually get the meat of the response.
So what I actually will end up writing in my app
when it's time to render a contact
or something like that
is a method like this showContact thing.
Where I somehow magically-- we'll discuss it--
got my hands on the service.
I've called this execute method.
I've given it an instance
of the GetDetails command that I want it to run.
And I've instantiated my callback GotDetails
so that once I've got all my information,
I can call renderContact, I can call renderDetails,
and go mess with widgets and stuff.
As Java goes, that's fairly concise.
We don't have anonymous...
we don't have closures and probably never will.
But this is about as short as it's going to get
in a Java sort of world.
So, on with the theming.
I mentioned that you want to decouple your app.
The whole left hand/right hand don't talk situation.
The way that we're going to make that happen
is the combination of an Event Bus,
using the MVP pattern
when we have custom widgets to implement,
and by using dependency injection,
preferably via a tool like Google GIN,
which is derived from Google Guice,
which all of you-- all of you
should be attending the Guice tech talks
that follow this one, by the way,
since you're already blowing off the Wave guys.
Guice, Guice, Guice.
Let's go into depth on each of those.
Oh, the reason that you're going to do this
is because it buys you things.
Because your app is nicely encapsulated,
you're going to be able to throw away bits of it
without the entire thing falling over.
You're going to give yourself a spot
where you can delay, as long as possible,
the slow DOM operations
that you might want to be-- required to perform.
Your application logic will be separated enough
from your widget and DOM manipulation logic
that the slow things can happen later or never.
You're going to find that unit testing
is a lot simpler to do.
If I can-- if it's very clear
how I instantiate this particular part of the app,
it's going to be easy for me
to test that particular part of the app
without having to swap in the other 500 parts of the app.
And if I do this right,
I'm going to have really fast unit tests
that I can execute in a JRE rather than in a GWTTestCase.
So unit tests that execute in microseconds
rather than in the 20-whatever seconds
it takes to launch GWTTestCase these days.
Which time we are improving, let me say.
But it'll never be the same as simply not doing it at all.
So what's an Event Bus?
Let's show you first what an Event Bus is not.
You're writing your app in a typical style,
and you've probably created yourself
a bunch of model objects in our address book
that are probably contact groups and contacts.
And they got property lists.
They're throwing property change events.
Each one has a list of listeners
that are watching for changes to the thing.
And we'll start adding in bits of our UI.
We've got our contact group list UI,
which is going to have a listener attached
to each one of these group objects.
And then we've got the list of contacts
within an individual group that's listening,
attaching handlers-- event handlers
to some of those contact guys.
We probably have a display with individual contacts
that the list UI needs to know how to swap in.
And the contact list is going to want to edit these things,
and these are all going to be listening to each other
for update notifications, you know,
show yourself, hide yourself, I'm done now.
And then marketing comes in and says,
"We don't have enough bread crumbs on this thing,
and that Window Title's looking kind of sloppy."
We got kind of a messy world here.
We've got a lot of handler objects that we've instantiated.
We've got a lot of things that we can forget to decouple
when it's time to tear things down.
It's not a fun world.
I won't even talk about testing.
It's an excellent profile.
So we can reduce this coupling in a couple of steps.
But first, we'll introduce our Event Bus.
This thing on the side of the app
that everybody has access to, where they can listen
for the stuff that has happened in the application
that might be of interest to them
without necessarily needing to know
who was responsible for that event in the first place.
So, for example, when the contact list UI
wants to tell the world,
"Hey, the user thinks that it's time to look at the details
of contact X,"
it can just post that event to the Event Bus.
And whatever parts of the UI are responsible
for actually showing the user contact display,
contact number X can take care of doing that.
And it doesn't have to know that it's--this week,
it's the contact list UI that's responsible
for doing that, even though next week
it'll probably be some other gesture
that some UI designer--
respected UI-designing colleague--
came up with.
But you see we still have a lot of mess
in the middle there.
We've got all these property change listeners
that I keep clearly implying
I don't think you should have.
And what I espouse doing instead
is just getting rid of that model core.
Not saying that you don't have model objects,
just saying that they're not terribly of central importance.
Instead, remember that we know that everything that we do
is asynchronous.
Let's just push that asynchrony
right through to the bottom of our application.
When the contact display UI is ready to display somebody,
it'll tell the RPC service,
"Hey, get me contact XYZ. I've been told to display him."
With any luck, that's cached,
and so my callback fires right away,
and I've got him immediately.
When the editing UI needs to mess with that contact,
and change his data,
he's going to tell the RPC service,
"Hey, I've got an update for you.
Push that down to the service for me."
When the response comes back in to the RPC service,
he will announce to everybody who is interested,
"A contact has changed.
"Here is the new copy of that contact.
Why don't you all who are interested display that thing?"
And everything is nicely isolated from each other.
So let's look at what the code would shape up as for that.
You'll remember this showContact method
that we wrote earlier.
We're going to add a little bit to it.
We're going to start paying attention
to just which contact we're looking at at the moment
over here in our contact display UI.
So just something as simple as holding on to that Id object
so that we know who's important to us.
Before we ever tried to show a contact,
we attached ourselves to the listener bus.
Sorry, the Event Bus.
We're going to use a GWT class
to actually instantiate our Event Bus,
because that's simpler than writing our own.
With GWT 1.6, we revamped our event handling mechanism.
And at the heart of each one of our widgets
is a handy class called the HandlerManager.
There's no reason that you can't use
your own instance of HandlerManager
to provide your app-wide event dispatch services.
So our contact display has somehow magically
gotten its hand on the HandlerManager
Event Bus instance for this app.
And he's attaching to it a typical event handler.
You've created for yourself a custom ChangeEvent type
called ContactChangeEvent.
And you're implementing
your custom event handling interface.
So when a contact change comes in,
you're going to look at the new contact object
that's hanging off of that event,
see if it's the one that you're actually displaying,
and if so, do something about it.
And a subtlety here is that you don't really know
if that's the one and only copy of this contact,
if it's one that's been handed to you
for your purposes at this moment.
The contact goes from being of central importance
where you might be attaching handlers to and from it
to this kind of ephemeral, disposable thing
you can kind of wad up and toss
on the floor when you're done.
But don't change it,
'cause other people might be looking.
Continuing with the code,
let's move over to the RPC service itself.
What we're doing here is we have wrapped the GWT RPC service
that we defined with somebody who's implementing
the interface itself.
In this particular case,
we're taking that execute method that our RPC service provides
and we're overloading it to provide specialized handling
of this UpdateContact type of action.
And the interesting bit is that we are wrapping the callback
that the caller gave us with our own callback,
which on a successful response is going to update
whatever kind of a cache we're maintaining.
It'll fire off the real onSuccess result
for the real callback,
and then it's going to tell the rest of the application,
"Hey, contact changed.
You might want to do something about that."
It's as simple as that.
So continuing with our decoupling theme.
I keep saying "MVP, MVP, MVP."
And I did not say MVC.
Now the odds are that when you got out
of the five or ten years you spent doing JSP
and Web 1.0 app,
you kind of dusted off some desktop knowledge
or somebody who had once done that kind of work
told you, "You know what we should really be doing
"now that we've got these widgets
is model, view, controller."
And it's this kind of triangular thing.
And the model throws events.
And then the view throws events.
And the controller messes with both of them.
And I forget what code goes in the controller
and what code goes into the view.
And no two people will give you the same answer
for what MVC actually means.
There are a handful of guys who wrote Smalltalk-80,
but they may well be retired.
You also wind up with a problem here
that, like I said, some of your code
landed in the controller.
Some of it landed in the view.
You need to test it all.
And the code that landed in the view
is not going to run real fast.
It's messing with the DOM.
And you either need to have a pretend DOM there
if you're lucky, which we don't provide you--
yet--
or you need to fire up a real DOM.
And those tests are just not going to run fast.
So what you want to do instead
is use the model, view, presenter pattern.
We found ourselves implementing this style kind of by accident,
just trying to isolate our code from tests.
And then some people who are brighter than me
were reading Martin Fowler's blog
and saw that he had already invented this pattern
before most of us were professionally active
and had called it MVP.
So we call it that too.
Then what you're doing is just much simpler layering.
Your view class down there at the bottom
is the only thing that actually knows anything
about DOM elements and widgets.
And it's a source of things like KeyboardEvent
and ClickEvent and very browser-specific events.
The model at the top,
we're familiar with at this point.
It's this kind of overglorified bag
of setters and getters that we're using
because we decided we don't like
property change listeners.
And that leaves the presenter in the middle
as the only object that has anything
that's really worth testing.
This is the thing that knows how to take, say,
a contact in one end and take its values
and cram them into a bunch of text fields
on the other side, listen for changes
to those text fields,
and interpret them as something--
as values to push back into the model.
When it comes test time, we can ignore
that silly little collection of DOM widgets that we created
and replace it with a bunch of mocks.
And in fact, we can even use the GWT MockUtilities package,
which I think was also introduced in 1.6,
to use something like EasyMock
or JMock-- or actually,
there's something called Mockalicious now,
which looks interesting--
to provide fake test fields, fake this and fake the other,
and run them under a JRE.
Although today I'm actually not going to show you doing that,
because I found that for the pretend code
that I'm putting together, it was easier
to do the mocks by hand.
So let's get concrete again.
In our address book, we are doing things
like editing contact details.
Say we're going to edit somebody's phone number.
So a phone number looks like that,
it's got an Id, it's got the telephone number,
and it's got the work, home, mobile, babysitter label.
The UI probably looks something like this.
I'm sure we've all seen that numerous times.
And all of the interesting stuff
is here at the presenter layer,
where we're going to implement
a thing called the PhoneEditor,
whose job in life is to take a phone object
in one end and a phone UI in the other end
and make them all go.
So the PhoneEditor probably looks something like this.
At least it does when I write it.
Oh, the brace is in the wrong place.
Damn.
It's going to define an interface
for the kinds of widgets it wishes it was talking to.
And you'll note that I'm describing things
like a save button and a cancel button
and a number field.
But I'm not calling them "button."
I'm calling them HasClickHandlers
and HasValue<String>.
These are characteristic interfaces
that the GWT widget set is littered with.
And if I define my display interface
in terms of these characteristic interfaces,
it becomes quite easy at test time
for me to provide mock instances of those things.
A lot of people think that providing this display interface
is more trouble than it's worth.
And they will tend to give their presenter objects
actual knowledge of the specific widgets,
like text field, like button.
Not a big deal, because you can, again,
use EasyMock or its like
to put in fake versions of those in the JRE.
But that made the slides too big,
so I didn't do that today.
So we've got our display interface.
How are we actually going to use the thing?
At some point in the lifetime of our PhoneEditor,
we'll be handed our display object,
which--don't tell anybody-- it's a composite.
We'll hold on to that thing.
We'll attach a few handlers to it.
OnClick of the save button, I want to call my doSave method.
OnClick of my cancel button, I'm gonna do my cancel method.
Sooner or later, somebody's going to tell me
to edit a phone number.
And I'll do that simply by holding on
to the phone instance that I'm editing.
I'm implying a kind of sneaky thing there
with that static method on phone.
I'm figuring that the instance of phone
that I'm editing may not turn out to be
the one that I actually want to keep.
And to make sure that I'm not going to accidentally
mess with the phone object that other parts of the app
might be trying to display,
I'm going to assume that I defined
a kind of a clone style method on my POJO.
You can follow this convention or make up something else.
This is more or less what we did,
except our code was uglier.
Once I've got this safe copy of the phone,
which I can toss away when I'm done
because model objects aren't special anymore,
I'll just tell my display to show the number
and update the label picker and that kind of thing.
Oh, there's another cheat that I'm doing in here.
I have implied--
head back to our interface here--
I have implied that GWT has a pop-up widget
that implements the HasValue interface.
It does not. We ran out of time.
I think it will have such sooner or later.
Maybe even in the 2.0 timeframe.
I need to talk to my boss about that.
But in the meantime, it is no big thing
for you to write your own HasValue wrapper
around the list picker as you're going to use it.
We did.
And it didn't take us hardly any time at all.
So full disclosure out of the way.
Now let's actually save what the user has done.
Again, it's a simple thing to pull the values
back out of that display instance
and then fire off my nice asynchronous service method.
When I hear back that my POJO--
my phone number was updated successfully,
I'll tear myself down, whatever teardown means
for my PhoneEditor.
If there were errors,
I'll have some error reporting
and rendering convention that I will have implemented,
and I'll display those things there.
Obviously, I'm glossing over a lot.
And you'll call me on that at question time.
But until then, I can just get away with this.
So let's talk about dependency injection.
I'm sure you've all heard the term.
And you've all been meaning to adopt it,
and it seems like a good idea,
but maybe on my next project.
Dependency injection ain't no thing.
It is just a pattern.
All it means is that instead of having global instances
of my services that I reach out for,
like GetEventBus.singleton instance,
GetRPC.singleton instance.
And instead of using a service locator
that I kind of cram full
of every interesting property of my app
and shove it down the throat of any part of the app
that might want it,
I'm very explicit,
preferably via constructor arguments,
about just what it is this particular object needs
to exist in a useful way.
DI is really not at all hard to do manually.
And in fact, AdWords does do all of its DI manually,
because we didn't have any way to do it automatically
at the time that we started the project.
In the interim,
there was a GWT developer on another project...
this is public.
You know the contact manager in the personal profiles
that Google is starting to flog these days?
The group that put that together were a big fan
of the Guice dependency injection framework for Java,
and they wrote their own version of it,
called GIN.
And now more and more apps, internally and publicly,
are taking advantage of that.
You can find both Guice and GIN
at the URLs provided there.
There are also talks coming up on Guice right after this one,
and if you leave this room,
I suggest you go to that room.
I'm not sure which one it is,
but it's on your little badge-y thing.
I had a picture of Snoop Dogg here
for the GIN and Guice thing,
but the lawyers wouldn't let me do it.
So, sorry.
So what does the code actually look like
when we do something like this?
Well, let's start at the bottom of it.
Let's assume that we have a widget
that is a widget in the MVP style
that is my contactViewer.
And that's kind of the heart of my app.
And when the contact viewer is ready to go,
it wants to get the root panel
and just start to jump into it.
So what do I need to do
to instantiate my contactViewer?
Well, I'm going to look at its constructor arguments.
It needs its ContactWidget,
probably implementing its display interface,
because I wrote it.
It's going to need an instance
of that PhoneEditor MVP presenter thingy
because it's going to-- it uses that end line.
It doesn't go talk to it through the Event Bus.
Not everything needs to talk to everything else
through the Event Bus.
Things that want to deal with each other directly--
there's no reason that they can't have
handles onto each other.
We still like composition in this world.
It needs the RPC service to ask for the contacts,
and it needs the Event Bus to listen for changes
to the contacts that it cares about.
So it's going to get its hand on the ContactWidget
with a simple "new."
It's going to get that PhoneEditor
with a simple "new"--ooh.
But the PhoneEditor has dependencies too.
It's going to need a widget
and it too needs its hands on the RPC service.
But we're not really using
the actual GWT generated RPC service.
We have our wrapping thing around that.
So let's instantiate our CachedBatching rpcService,
into which we will jam the one that we GWT.created
to do the real work.
And then finally, everybody seems to want
this Event Bus thing,
so we'll instantiate one of those.
So that's kind of a lot of code,
but it wasn't really hard to look at.
Part of the payoff starts to come
when new dependencies appear.
Marketing has heard about this Google Voice service,
and they're absolutely convinced
that we should have Google Voice integration
with the PhoneEditor.
So we're going to add that
by instantiating the voice service.
We're going to change the PhoneEditor constructor.
And nobody else knows about that change.
Even though the PhoneEditor is fairly far down
in our tiny little stack,
the thing that needs a PhoneEditor
because it never instantiated it itself
doesn't need to know
that PhoneEditor now needs the VoiceService.
And I didn't have to add a VoiceService argument
and a VoiceService argument and a VoiceService argument
at every level in my code.
But still, you can see that this is going to grow
into a lot of kind of grungy code to maintain,
and a big list of instantiation
to do all at the top.
If you were to use a framework to do it,
something like GIN-- talk--
Actually, there's no talk about GIN itself today.
I don't want to imply that.
But there is a talk about Guice.
And GIN is just like Guice, but on the client side.
Anyway, if you use GIN correctly,
and using it correctly is easier
than using it incorrectly,
the code winds up looking like this.
So I highly recommend it.
Now why are we doing all of this stuff?
What's the punch line?
Besides having the app isolated from itself
for that purposes,
we're particularly happy because this gives us
easy to write and fast tests.
So to write a test in this style,
we need our mock objects.
We're not using EasyMock here,
so we're just implementing these things ourselves.
We're going to make stuff that looks like this.
I'm going to have a mock version
of my ContactsService.
And it's going to have these fields here,
the lastAction and the lastCallback,
and I'm going to fill those at obvious times.
When somebody gives me a pretend execute this object,
execute this action call, I'll hold on to it
so that my test code can take a peek.
Throughout the code examples here,
I've been leaving off things
like public and private and so forth so on.
On this slide, that's not pretend.
I probably would have package access
or even public access
to the fields on this mock object
because there's not a lot to be gained.
By having accessor discipline,
I wind up with more concise code.
We're going to need a few more mock objects
as we're working through here.
So I'm going to have to have
a nice fake version of that ClickEvent.
Because for reasons that I don't remember,
we decided you shouldn't be able to do new ClickEvent.
But let's not worry about that.
We have a mock version of the thing
that has ClickHandlers.
This is our pretend button.
It's got the same notion of the last ClickHandler
that was added to me.
I'll hold on to it so that people can find it.
When I'm told to...
Does this do anything else interesting?
Nope.
Yeah, in fact, the fireEvent, it doesn't even do anything.
It's a no-op. Our tests don't tend to do that.
Similarly, we had our HasValue thing.
We've got a mock version of that
that'll hold on to the last value
that was set into it.
It also, because I have access to...
Never mind.
That's not a point worth making.
And then finally, we'll put together
a bigger, more interesting composite mock object,
the thing that implements our PhoneEditorDisplay.
So where we would have needed a phone editing widget,
remember that our PhoneEditor presenter
wants a phone edit widget.
We gave it a nice instance of MockPhoneEditorDisplay,
which is made up of MockHasClickHandlers
and some MockHasValues.
Oops.
Oh, well.
Some fake accessor methods for those,
and again, nice visible fields
so that the test can play with it.
So that's a lot of work that we had to do
for what's going to be a test for three lines of code.
On the other hand, the three lines of code
will probably be more interesting in real life
when I need to test, say,
"Did I warn the user that they're about to abandon
their changes when they navigate away from this page?"
And when I have tests for the cancel path
and for the save path,
all of these guys are eminently reusable.
In fact, you might be asking yourself,
"Why doesn't the GWT team provide me
"with a prerolled MockHasValues
and MockClickHandlers?"
And yeah, we should probably do that.
So on to the test itself.
We're going to put together our mock world.
We've got our fake service.
We've got our fake display.
We're creating our fake phone object
that we wanted to be editing.
And we're setting--actually, it's a real phone object
that we wanted to be editing.
And we're setting up another one
for how we expect the "after" phone object
to look that we're going to send across the wire
for comparison purposes.
We're going to instantiate the PhoneEditor,
the thing that we actually want to test here.
We're going to tell it "Go edit this phone number,"
just like we would tell it in our real code.
And then we're going to sneak into our mock objects.
We're going to tell the labelPicker,
"Okay, the user typed 'work.'"
And we're going to tell our number field,
"This is the phone number she provided."
And then we're going to tell the fake save button,
"Do a click for me now, okay?"
That should have been enough to kick off
our presenter object, the PhoneEditor.
And now we're going to verify all of that.
We're going to sneak into our service.
We're going to see what action it actually requested.
We're going to make sure that it was an instance
of UpdatePhone, based on this expected...
Oh. That'll work, actually.
It should've been UpdatePhone before,
but they're equivalent
because they have the same contactId
is the idea.
And I did run this test.
It compiles and passes.
Buh-buh-buh-buh-buh.
And then we get out of the action.
The phone object that was actually sent across the wire,
we compare the interesting bits of that
with what we expected to see, and our test works.
So, um... oh, blew the transition.
There we go.
So finally, I talked about you wanting to have
statelessness going on in your app.
And what I'm talking about there
is you want to have a lot of disposability.
When your server falls over,
your user doesn't want a notice about it.
What I'm saying here is,
you really do want to have stateless servers.
And in particular,
the--doing anything interesting based on the session,
the Servlet session object on your server,
is just a bad idea.
It's a fine thing to use for caching.
Caching is a wonderful thing.
But anything--
If you're coming from a JSP background
and you've written a multi-step wizard--
you know, step one, step two, step three--
there's a good chance that you implemented that
via some kind of session object on your server.
And your user's sort of halfway through your wizard
when the server falls over or its refresh time--
they're all screwed.
Or you wrote something really confusing
and difficult to maintain to try to bounce
that session state from one server to another,
and that was more trouble than it was worth
and pagers go off and stuff.
Don't do that anymore.
You've got a browser now. You're an AJAX developer.
The browser provides all the server state,
all the server session you actually need.
So that means that when it comes time
for rolling updates and servers are falling over
and new ones are taking their place,
or it means that at your desk,
when you need to restart your server
because you rejiggered some of the business logic code,
what's going on in your browser
doesn't even need to notice that that happened.
Maybe some calls will be a little bit slower
because the cache fell off.
But otherwise you're fine.
If you're developing an App Engine app,
you've got the Memcache package that's available for you
which works really, really well for this kind of thing.
So we've got disposable servers.
We similarly want to have disposable clients.
I'm afraid I'm going to gloss over this
more than it deserves,
but the punch line here is,
as I said at the beginning,
you want to get your history right.
As your user is doing things
that feel like navigating around your app--
you remember in the AdWords demo,
I was clicking around the tree
and clicking on-- navigating
into different parts of the app.
Each one of those places in your app
wants to be embodied by a GWT History token,
so that as the user hits that back button,
she'll return to where she was.
If she hits the forth button, she goes on.
If she keeps a bookmark, that bookmark actually works,
and she returns to the space where she was in the app.
If you've done that right,
you've got a disposable client.
When the user hits F5,
or you copy a URL from one browser to another,
it just works.
Your development life is happier.
Your users are happier.
That said,
the History library is in the traditional GWT way.
It is a bit low-level.
Making every bit of your app knowing about History tokens,
and knowing to listen for OnHistory token change,
and to try to push History tokens
if they want to change is sort of nasty.
And you want to come up with some kind of an abstraction
to isolate the bits of your app
from that kind of knowledge.
What we did in AdWords was we came up
with a package called Place.
It's not open sourced.
It wound up being very ad-specific.
But the pattern itself is pretty straightforward.
It pretty much winds up being, just like I have
command objects, action objects to embody operations,
I've got place objects to embody addresses within my app.
They're kind of incarnate bookmarks
and each place object knows how to talk
to the History service, to push tokens,
and read tokens and that kind of a thing.
And it's all integrated into the Event Bus.
So you remember this picture, where all of my bits of my app
are talking to each other.
To introduce place management into this,
I'll create something called the place service,
which is the only part of the app
that's actually responsible
for knowing about the GWT History library.
So when a bookmark is pasted in,
when the back button is hit, when the forth button is hit,
place service notices about that.
And it announces to the rest of the app,
"The user has changed places.
Here's the new place that they're going."
Maybe it represents a contact.
Maybe it represents a text ad in AdWords.
And the bits of the app that need to swap themselves in
or swap themselves out can listen
for those more semantic objects, the place changes,
and go on their merry way.
I've actually run out of things to talk about.
Like I said, it always ran long before.
So I did talk faster.
But we have ample time for questions and answers now.
So if people could line up at the microphones,
so I don't have to try to repeat your question
and get it wrong,
this would be an excellent time.
[applause]
Thank you.
man: I just wanted to reiterate
something you mentioned briefly, that...
about creating some of these mock objects
inside of GWT.
And not so much a question, but a hope
that that is something that would happen,
because anything we can do to make it easier
for testability and to get these designs right,
I think that would really help with adoption.
Because in past projects, we've tried to use it,
and it's really difficult to get it right
and to get the decoupling in.
So if that was baked in to just standard GWT,
that would be, I think,
a really, really beneficial adoption part.
Ryan: We totally agree.
And we're getting better--
learning more about that kind of thing
as real people build real applications
inside of Google and yell and scream at us.
Before we get to actual changes inside of GWT,
one of the things that I'd hoped to deliver today
was a simple sample app
that actually embodies all of these practices.
So if nothing else, there's code to point at
and show people, "No, do it that way."
And I'm going to try to block out some time
to actually finish that thing and post it
up on the GWT website sooner or later.
man: First I just wanted to say thanks.
All the things in this talk
were definitely things I've been struggling with.
Ryan: Great.
man: I was about halfway there,
and this kind of-- going to help me fix things.
I was just going to ask about testing and mocking.
I've seen a lot of discussions back and forth
about is it feasible to test the GUI itself.
So the techniques here are great.
I didn't think about doing it where you're actually
getting most of your code out of the DOM
and you're just testing everything else.
But yet, let's say you're trying to make
a very complicated, you know, interface.
Drag and drop, resizing, crazy stuff.
Ryan: Yep.
man: How do you test that?
Ryan: Then you're back to a GWTTestCase.
But, you know, it's not like GWTTestCase is poison.
You've got a--kind of a 20-second startup time
to get it going, but each individual test
beyond that actually runs pretty quickly.
And as long as you've got...
We've got a discipline within Google
about small, medium, and large tests.
And I think for a GWT app,
that breaks down into JRE-type unit tests
are the small tests.
You want those to be about 70% of your test coverage
in an ideal world.
And GWTTestCases are medium tests.
The ones that, you know,
they're a little bit more complicated,
but they're still not involving
setting up the entire world.
If I'm writing a widget that has enough DOM-based code
that it'd be worth testing,
I'll write a sample app.
Or just a display of that.
And I'll have a GWTTestCase that drives that thing.
And then the third part of that puzzle,
the large tests,
for us, that tends to mean Selenium tests,
where I'm actually going to put my entire app together--
or maybe put together some subpart of my app together
if my app is too big to want to fire it up
every time I test on everything--
and have kind of happy path tests
that make sure that I can flow through the entire app
and off to other pages and back again.
That sort of thing.
There's also work being done
under the Selenium umbrella called WebDriver--
which I think is going to be rechristened Selenium 2.0--
that provides much better browser control.
Actual little plug-ins
that can go inside of IE in particular.
So that instead of having to pretend to do a click
and find that you emulated it wrong,
it will do an honest-to-God click
of an honest-to-God button.
You can test off of that.
So, 70-20-10. Small, medium, and large.
man: Would that new Selenium stuff
work in conjunction with the in-browser hosting mode
where it's kind of built in right now?
Ryan: Yeah.
man: 'Cause the stuff that's in there now
is kind of-- there's docs on it,
but it's not really well exposed.
Ryan: We're still getting our story straight
on how to get those all to play well together.
Another fun thing that we started looking at
is the HtmlUnit package.
They've been, for quite a while now, saying,
"Hey, guys. We work with GWT apps."
And we're looking to actually take advantage of that fact
in GWT for you.
So that when you do stuff like run a GWTTestCase
that doesn't actually care about accurate layout
or that kind of a thing,
you can talk to the pretend browser
in this HtmlUnit package
and get faster execution that way.
But still even with that,
the more you can isolate application logic
from view logic,
the simpler and faster your tests are going to be.
man: We're using the Event Bus approach, partially.
I'm curious what you do if a bunch of objects
that get put on it that aren't being used anymore--
they're basically dead listeners
that are added but never removed.
And how do you manage that?
Ryan: Uh, badly.
We don't have weak references.
This isn't really Java. It's just a pretend Java.
So you pretty much have to...
It's all developer discipline of listening--well, actually,
the place change event simplifies that some.
Because we've got that at the heart of our app,
widgets are able to hear,
"I'm not relevant right now, and so I'm going to--
"When a place change happens,
"and it's not a place that I live in,
"I'm going to tear down my Event Bus handlers
and throw those away."
We...of all of the many fascinating performance
and memory problems we have run into
in the course of AdWords, I don't think that that one
has proven to be too big of a deal.
I'd put that down to having that kind of consistent
place handling story.
man: Okay, related to that,
if I have multiple tables and I have an object
that's interested in events from one of the tables
and you want to use this Event Bus approach
to communicate that,
how do I know which table it is
without being directly connected to them?
Is there--have you guys solved that problem?
Without knowing the object itself.
Loosely coupling.
Ryan: Yeah. I'm not following the question.
man: Maybe it's too complex.
Ryan: If you want to come up afterward,
we can hash it out.
man: I just thought of-- the other question I had--
So I went through the whole property change listeners mess.
And I'm looking forward to getting rid of them.
But how do you deal with efficiency?
Like, now you get an event that this object changed.
And you have to update the UI to reflect it.
But you don't know exactly--
if you knew, like, this one field changed,
you can just go and change one field
or move the corner over.
But now all you know is something changed.
And how do you get around...
Ryan: Well, I know this...
I know the something that it is
that is actually interesting to me.
And you've got more efficiency now,
because instead of having this one-to-one mapping
of everything that wants to listen to something
and what they're listening to,
I've got a lot fewer instances of handler.
The execution speed, I don't think,
has turned out to be such a big deal.
Especially if I'm good about only listening
while it makes sense for me to listen.
So if I'm the tree of contacts,
just, you know, then I'm displaying all of them,
well, every single contact event
is going to be of interest to me.
If I'm the editing UI that's only displaying
a particular contact at the moment,
it's very unlikely that some other contact changed,
because I'm only one application.
I'm not, like, some kind of server
handling all of the stuff.
man: And what if there is...
Our app has, a lot of times, more than one view
on the same object in different ways.
Ryan: But you're still-- you're usually not
going to have hundreds of views, right?
There have-- two or three views,
and those two or three views will be listening
and maybe will hear some irrelevant stuff.
I think that that'll all wash out.
man: All right. Thanks.
Ryan: Hi.
man: Yeah, my question is,
you talked about the importance
of being able to bookmark or refresh pages.
So going with the contact example,
you know, it's not just, you know, good enough
that I'm on, you know, hit the back button
and it knows I was at the contact page.
You know, I want to hit the back button and know
I was viewing, you know, John Doe.
And then I want to hit the back button again
and know, you know, it was Jane Smith.
So, you know, I'm kind of wondering,
like, in the HTML or in the Web 1.0
or JSP world, you would pass URL parameters.
You know, with a question mark Id.
Ryan: That's pretty much how
the History library's implemented.
Except everything winds up after the hash mark
rather than after the question mark.
So what you wind up doing
is I've got a subclass of place
per type of object that I want to be--
that my location is relevant to.
So in the AdWords case, we wind up with
campaign places, ad group places,
and stuff like that.
These place objects know enough
about the History management system
to provide a History token string
that includes the ad group Id and the campaign Id.
And so boom, it's right back there
in the URL.
man: And so then does the place interact
with the Event Bus, so when it notices...
Ryan: Yes.
man: That a new Id was passed, it triggers an event?
Ryan: Exactly. man: And notifies pages?
Ryan: So we can go back to our pretty picture here.
That pretty picture.
So the user hits the back button or the forth button.
The History event hits the place service,
which is where the code that actually implements
the History client interface lives.
The place service instantiates a new little place object,
which...let's see.
The place service might actually talk to your RPC service
to, you know, say, "Get me this contact
that is relevant to the URL that I just had."
And then it will announce to the world,
"Hi, everybody, we're at a new place.
And this is the contact object that I found at that place."
Nobody else has to know about the fact
that it happened to live on the URL
or whatever else went on.
man: And then, you know, around your Event Bus,
how-- I'm just kind of interested
in how many different things
that subscribe to a single event,
like, you know-- let's say I have
a tiered navigation, you know, maybe.
Or I have top navigation and side navigation.
So kind of pages within pages with, you know--
or in terms of GWT, like a panel within a panel.
You know, maybe I have, you know,
a bunch of tabs at the top.
And I have a contact tab and I have tabs on the left.
I mean, does-- do you try and just subscribe
to the event in one place and then top down drive,
like, "Hey, this was changed,"
or do you have multiple places--
Ryan: No, it...
People did start talking directly to the Event Bus
all of the time for everything.
And that was kind of retrograde.
If I've just got--
It's like in the PhoneEditor example.
The PhoneEditor...
Or in the dependency injection example.
We created our contact display and we gave it an instance
of PhoneEditor display.
PhoneEditor didn't have to know
about navigation changes within the app
because it had a really tight relationship
with the contact display object that needs to know
how to edit phone numbers.
There's no reason for that to go crazy with it there.
So I don't have a pat answer for you.
But I think it's the...
the top-level bits of the app
that need to appear and disappear
are probably the ones listening to, for example,
place changes.
Maybe other types of events-- you know, contact editing
or whatever, maybe there you do want to be a bit finer grained.
But you kind of...
It becomes a feel thing.
I don't really have a simple answer.
man: So have you guys thought about
just taking a simple app like this
and putting it out there as an example?
Ryan: Yeah, absolutely.
man: 'Cause, I mean, you know, what's out there,
I think it was the sandbox or the--
Ryan: Yeah, um--
man: It's nice, but it's not really
real-world scenarios.
Ryan: I have been reliably informed
that I will be doing that.
man: Well, thanks. Appreciate it.
[audience chuckles]
man: Hi. Quick question.
I started recently using GWT, and I really like it.
My question is more about the History button in general,
since you've talked about different design paradigms
and patterns.
Is History button really, in your view,
still a useful thing?
Or not History button.
The back button, I'm sorry, I meant.
Ryan: Oh, sure.
man: Because now we're talking
more and more rich applications.
You know, fancy applications.
More like desktop applications.
You really--you know, once you add--
you may want to let them delete it,
but not necessarily delete by going back, for example.
So-- Ryan: I wouldn't--
You don't want to let them delete by going back.
But...especially if you're doing an application like this,
where at the end of the day, it's a big bag of objects
and I want to navigate around the space
that that big bag of objects defines,
your users are used to clicking on the thing
and then hitting the back button
to get back to the place that they were before.
I'm certainly not suggesting that you encourage--
that the back button becomes your undo mechanism.
I think you'll go insane.
But you can't stop your user from hitting that back button.
And it's a pretty nice way
to work your way through a space.
man: What we did in our app is basically
we would stop having the back button at all.
So as a result, user get used
to using in a different way.
More like they're used to using a rich application.
Ryan: I've never had the luxury of working in an app
where I was allowed to take the back button away.
So...but I wouldn't want to.
I mean, it's...
There's a reason that the Web browsers
got shaped the way they did-- it feels really good.
If you're--I mean--not every app is this kind of an app.
I don't know if the back button really makes sense for Lombardi,
where they're, you know, doing more--
I'm doing a big diagramming thing.
But for an app like this,
I think it's pretty natural.
And it's not something to hide from.
man: Thank you.
man: When you build the UI in GWT,
it's very developer-centric, usually.
It's basically Swing.
It feels like that when I'm building a UI.
But usually, UI designers want to make it with mark-up.
So there was some talk a while ago
about a declarative UI.
I may have missed it if that came out.
But what do you use for AdWords,
and are there--
Ryan: We use declarative UI. man: Yeah?
Ryan: The code's there. It exists.
It's called UiBinder now.
Its documentation is all public,
to make it painfully obvious
that we haven't actually released the code itself.
We are trying to actually get it out there,
and it's just been a question of too few engineers
being pulled in too many dimensions.
But yeah, UiBinder is used by AdWords.
Wave uses it.
And getting those guys to ship
has kept managing to trump
actually open sourcing the thing.
It will go out.
There's also, in AdWords, there's some code that we rolled
to try to-- I implied a lot of Glue there.
Of...here's my phone object, here's my string display,
copy the string to here, copy the string back,
and, you know, that kind of stuff.
We managed to generate some amount of that code
in AdWords.
We weren't super thrilled--
or I wasn't super thrilled with what we came up with.
But we've got notions there too
of frameworks we'd like to provide
to kind of minimize that need.
But I don't have particular promises
or particular dates to say.
We're aware of it as an issue.
And we'll get there.
man: Hi. Thank you for your talk.
Ryan: Thank you.
man: So the application that we work on,
we've done something similar to this
with something that roughly translates to the Event Bus.
But we don't have all the other constituent pieces
talking to the services.
Instead, they-- there's, like,
this back and forth propagation of events.
And can you comment on why that would be--
Ryan: If it's working for you,
I wouldn't tell you to change it
just for the sake of changing.
We...this worked nicely for us
because it...
I think we have no more events than we actually need
traveling around the place, and...
Again, you know, for the testing
and for configuration purposes,
the contact list can stand on its own
without having to know who, actually,
it will be calling into display contacts
and that kind of a thing.
If you don't have a problem,
there's no reason for you to find a solution.
So... man: All right, thank you.
Ryan: Yeah. Hi.
man: Hi. One of the classic patterns
that people use while writing UI code,
especially in JavaScript libraries
like YUI and other things like that,
is having data objects, i.e. beings,
that they can then take into a widget
that is able to take that data object
and using a reflection-like mechanism
of one sort or another display things
into the widget.
Manipulate the data,
potentially even edit it then spew it back out.
There's nothing like that today in GWT,
and I'm suspecting very strongly
that you guys internally at Google
have some way of dealing with this
for the large apps that you have.
And what are your plans in the future
with respect to this?
Ryan: We do have kind of part of that in AdWords.
Other teams...
AdWords is the...
maybe the only app,
or one of the only apps at Google,
that's actually this kind of classical,
J2EE-style app.
And so we don't tend to have broad solutions
for those types of things.
We have the solution that AdWords rolled
for their particular needs.
You've probably seen some borderline flame fests
on the GWT contributors list about data binding
and the need for doing something about this.
The approach that we have taken
is where you would have used reflection,
we will decorate bits of our UI
with annotations,
with the name of the particular property
of the model object that that part of the UI
wants to display,
and we can generate the code
that would have been reflection-based
for--this text field is tied to the name property.
Oh, this thing looks like a JavaBean
and it has a GetName method.
I'll GetName, I'll call SetName at appropriate times.
We want to make that better
to generate more of that code
than we're generating right now.
And there's also similar problems
that are coming up on the server side
with AppEngine and the ORM objects
that you're probably persisting through JDO.
A lot of people have found that they've created
these crafty little objects
that get all nice and persistent for them
on the server side, and then they try
to serialize them for GWT RPC,
and that doesn't work,
and how do I get around that
without making separate copies
and calling set and get and get and set back and forth?
I think there's going to be a similar solution to that,
but we don't--
We haven't quite come upon it yet.
But we are thinking about it actively.
I'm sorry, Fred, I was just told
I have to shut up now.
So thanks, everybody.
[applause]
Mobile Analytics