Strongly Typed Proxies in AS3
October 30, 2008 on 4:00 am | In Flex, Programming |Something I’ve often wanted in AS3 is a strongly typed proxy: an object that implements a known interface with typed methods, but where every method results in the invocation of some method like invokeMethod(methodName:String, args:Array). AS3 does have a Proxy class, but unlike Java’s class of the same name, an AS3 Proxy doesn’t implement any particular interface. One can extend Proxy into a subclass that has an interface, but by that time the Proxy superclass isn’t delivering any real value. The AS3 Proxy has been designed for loose, dynamic typing — which after all is one of the strengths of the ActionScript/ECMAScript languages.
Why use a strongly typed proxy? A common use of such a proxy is to create a “smart wrapper” around some underlying object with a known interface that looks and acts (and code-hints) exactly like the underlying thingamabob, but does some other stuff around every method call or property access. You might want a smart wrapper that logs or records all the function calls (as well as executing them). You might want a wrapper that does a try/catch around every call. You might want a wrapper that serializes the call over a stream to some remote destination.
(If this sounds like what some call “aspect-oriented programming”, well, it is similar to one aspect of aspect oriented programming :-)
Anyway, we can’t use AS3’s Proxy to solve this problem, so what to do? We’ll start with a simple approach, but make it more general. If our interface looked like this:
public interface ISmashable
{
function smash(force:Number):String;
}
and we wanted to create a smart wrapper that logged all function calls around any ISmashable implementation, we might create something like this:
public class SmashableLoggingProxy implements ISmashable
{
private var _smashable:ISmashable;
public function SmashableProxy(smashable:ISmashable)
{
_smashable = smashable;
}
public function smash(force:Number):String
{
trace("smash():", force);
var result:String = _smashable.smash(force);
trace(" result:",result);
return result;
}
}
Then, if we did something like var proxy:ISmashable = new SmashableProxy(realSmashable); we could treat the resulting proxy as an ISmashable everywhere in our application, Every time we called, say smash(5) method on proxy, not only would realSmashable.smash(5) be called but we’d see some nice trace output as well.
If you have the time to create a lot of wrapper methods like this (or write a fancy code generator to save the trouble), that’s fine. Another way to go, however would be to create a “generic” smart wrapper base class:
public class SmartProxy
{
private var _obj:Object;
private var _functionNames:Dictionary;
public function SmartProxy(obj:Object)
{
_obj = obj;
// set up Dictionary that goes from functions in this wrapper to function names
_functionNames = new Dictionary();
var typeInfo:XML = describeType(this); // returns metadata for subclass interface
for each (var functionInfo:XML in typeInfo.method)
{
var name:String = functionInfo.@name;
_functionNames[this[name]] = name;
}
}
protected function handleInvocation(args:Object):*
{
// 'callee' is a special AS3-defined property of an 'arguments' object
// that provides the Function which was called
var functionName:String = _functionNames[args.callee];
return applyFunction(functionName, args);
}
protected function applyFunction(functionName:String, args:Object):*
{
// This looks up the name of the function that was called, uses it to
// obtain the Function in the underlying object as a property, and calls it.
return (_obj[functionName] as Function).apply(_smashable, args);
}
}
Now extend this to implement our interface:
public class SmashableProxy extends SmartProxy implements ISmashable
{
public function SmashableProxy(smashable:ISmashable)
{
super(smashable); // sounds like a Nintendo game, eh?
}
public function smash(force:Number):String
{
return handleInvocation(arguments); // arguments is a special AS3 language keyword!
}
// (if our interface had other functions, they would all have the same body!)
}
The above SmashableProxy acts exactly like the underlying object. What’s the point? Well, given a base class of this kind, one can create smart wrappers of any variety with very little additional effort:
public class SmashableLoggingProxy extends SmashableProxy
{
public function SmashableProxy(smashable:ISmashable)
{
super(smashable);
}
override protected function applyFunction(functionName:String, args:Object):*
{
trace(functionName, "():", args);
var result:String = super.applyFunction(functionName, args);
trace("result:",result);
return result;
}
}
The astute reader may notice that logging is generic, and wonder if it should be handled in the SmartProxy, not in this subclass. So a further extension of this idea might introduce a separate invocation handler that can supply generic handling for all method invocations, and is a property of the SmartProxy. Java in fact has exactly such a class for its Proxies.
In another post soon, I’ll describe how this fits into the testing strategy for Noteflight, which has a large automated test suite of recorded UI actions. We use this suite to validate the program before every checkin — it is a very complex editor, and it’s all too easy to fix one thing and break another, so the test suite makes sure we don’t do that!
10 Comments »
RSS feed for comments on this post. TrackBack URI
Leave a comment
Entries and comments feeds.
Valid XHTML and CSS.
All content copyright (c) 2006-2007 Joseph Berkovitz. All Rights Reserved.


return (_smashable[functionName] as Function).apply(_smashable, args);
this line seems to wrong, shouldn’t it be
return (_obj[functionName] as Function).apply(_obj, args);
Comment by sebestenyb — October 30, 2008 #
Yes, it was wrong. Thanks for catching that mistake!
Comment by joe — October 30, 2008 #
keep on posting these cookies, i’m very curious about those testing suites, we badly need some similar stuff
Comment by sebestenyb — October 30, 2008 #
Joe, this is great stuff. I look forward to your follow up article.
Comment by Kyle — October 30, 2008 #
Hey Joe-
That’s a helpful post. It would still be a lot nicer if AS3 gave us the ability to create dynamic proxies without writing a new class, but your solution is about as elegant as it’s going to get with the current language. Thanks for writing it up.
If you’re so inclined, there is a bug logged with Adobe around creating dynamic proxies, if you (or your readers) want to vote for it, perhaps they’ll add support in the future. :)
https://bugs.adobe.com/jira/browse/ASC-3136
Cheers,
Adam
Comment by Adam Brod — October 30, 2008 #
Great post - Thanks.
Comment by Mansour — October 31, 2008 #
Constructor to SmartProxy class should be renamed ‘SmartProxy’ rather than ‘SmashableProxy’ :-)
Comment by Mansour — October 31, 2008 #
Can you tell that I never compiled this code? :) I have compiled stuff exactly like it though, at least.
Comment by joe — October 31, 2008 #
That’s brilliant. I was having the same problems with Proxy. I wanted to be able to wrap a class but still keep strict typing on the interfacing of it. (I luv me autocomplete and type checking).
Another cool use for this is somewhat like multiple inheritance, you could similar to the prototype chain, use this approach to merge/coordinate changes to two or more overlapping models (e.g. a container to manipulate groups of visual items).
Comment by TroyWorks — November 7, 2008 #
I’ve been working on AOP for AS3 in a project called Loom, and will be releasing a public beta shortly. There are a few code samples on the blog, and if you sign up for the private alpha on the discussion group I’ve been releasing the library in its current working form.
http://loom.ninjitsoft.com
The project is built for general-purpose bytecode weaving in AS3 for AS3, but I started with support for type-compatible AOP proxies because that was where I saw the most need for my own projects.
Cheers,
- max
Comment by Maxim Porges — April 20, 2009 #