A more concise approach to using GWT.runAsync

22 views
Skip to first unread message

Joe Cheng

unread,
Feb 17, 2010, 2:29:11 AM2/17/10
to google-web-toolkit, jarrod.carlson
If you've tried introducing multiple split points into your app, you might have found yourself cutting and pasting a lot. You might have tried getting rid of that cutting and pasting by introducing a common delay-loading abstraction. You probably discovered that this results in all the different split points you wanted, being mashed into one big one. So you went back to cutting and pasting.

At least that's the meandering path I (and several others on this list) took, before I discovered that you could use deferred binding to do all the dirty work for you, leaving your source nice and clean. I've tried to wrap up my efforts into a reusable package, which I've blogged about here:

http://jcheng.wordpress.com/2010/02/16/elegant-code-splitting-with-gwt/

There is a steeper learning curve on this than I'd like, hence the length of the post. If you have any feedback on the approach, the code, or the writeup, I'm all ears...

Thanks-
Joe Cheng

jarrod

unread,
Feb 17, 2010, 11:04:31 AM2/17/10
to Google Web Toolkit
First of all, I applaud your efforts and you've definitely done a nice
job of documenting your findings. I will offer a few comments on the
solution, though.

I like that you've taken the idea of my PresenterProxy (http://
groups.google.com/group/google-web-toolkit/browse_thread/thread/
6ec343860253c621/20ab002804a6ee6e) and expanded it to support most any
class imaginable.

However, a side effect of this solution is that dependent classes
(your ButtonPanel, in this example) must now be updated to rely on the
*Shim class instead of the original class used before the shim.

One of the tenets of dependency injection is that you should be
injecting dependencies according to interface, not implementation (see
Martin Fowler's explanation: http://www.martinfowler.com/articles/injection.html#InterfaceInjection).
So your dependent classes shouldn't have to change when your
implementation changes, nor should they care what type of
implementation they receive.

So in that regard, while using the shim may provide a nice way to
abstract some boilerplate, it does break your existing API contracts
and it requires that you define new *Shim contracts that are likely to
duplicate existing API contracts.

If the underlying implementation in the shim is to be created and
delegated to asynchronously, then it is true that each shimmed method
must return void (similar to the RPC async interfaces), and that
imposes some limitation on what we can readily abstract.

But perhaps you are on to something with the use of deferred binding.
However, maybe what we should be looking at doing with the deferred
binding is utilizing a generator that can (at compile time) generate a
unique subclass of the original class capable of performing the
asynchronous grunt work automatically. Perhaps if I have some time I
can toy with that idea...

Joe Cheng

unread,
Feb 17, 2010, 2:21:06 PM2/17/10
to google-web-toolkit
Thanks for taking the time to read it and provide feedback! Comments inline.

On Wed, Feb 17, 2010 at 8:04 AM, jarrod <jarrod....@gmail.com> wrote: 
However, a side effect of this solution is that dependent classes
(your ButtonPanel, in this example) must now be updated to rely on the
*Shim class instead of the original class used before the shim.

One of the tenets of dependency injection is that you should be
injecting dependencies according to interface, not implementation (see
Martin Fowler's explanation: http://www.martinfowler.com/articles/injection.html#InterfaceInjection).
So your dependent classes shouldn't have to change when your
implementation changes, nor should they care what type of
implementation they receive.

So if you're following this tenet, you have Module (interface with public API--and ONLY the public API, and by "public" I mean available to consumers on the far side of the split point) and ModuleImpl (class) today, right?
 
So in that regard, while using the shim may provide a nice way to
abstract some boilerplate, it does break your existing API contracts
and it requires that you define new *Shim contracts that are likely to
duplicate existing API contracts.

I didn't get into this with my blog post, but if you do already have Module and ModuleImpl, your ModuleShim can merely declare that it implements Module. No need to re-declare the API. The deferred binding code will find ANY method that returns void and is not implemented.

In other words:
class ModuleShim extends AsyncShim<ModuleImpl> implements Module {}

If I'm understanding you correctly, nothing needs to change with the callers. But you do then need to change your Gin module binding to point to ModuleShim.
 
If the underlying implementation in the shim is to be created and
delegated to asynchronously, then it is true that each shimmed method
must return void (similar to the RPC async interfaces), and that
imposes some limitation on what we can readily abstract.

That's true, but no more so than any other solution where code outside of a split point needs to cross the split point boundary. If you want a result value, you'll have to pass in a callback rather than just use a return value.

Code that's on the same side of the split point as ModuleImpl can continue to call methods on it directly and synchronously.
Reply all
Reply to author
Forward
0 new messages