Die in ActionScript 3 eingeführte Rest-Syntax, die eine beliebige Anzahl von restlichen Argumenten in einer Funktionsdeklaration erlaubt, kann ganz praktisch sein, wenn man nicht weiß, was kommt. Ein Einsatzbeispiel ist NetConnection.call(), das neben einem String und einem Responder-Objekt eine beliebige Anzahl an Folgeparametern erlaubt. Was aber, wenn man diese Argumente delegieren möchte?
In meinem Fall hatte ich eine Wrapper-Klasse geschrieben, die mir die Verbindungsdetails zu einer externen Datenquelle kapselt. Auch das Responder-Objekt wurde komplett privat verwaltet. Den eigentlichen Aufruf wollte ich aber flexibel halten, so dass der NetConnection-Call nach außen hin in einer eigenen Funktion public gemacht wurde. Das bedeutet, dass meine call-Funktion als call(cmd:String, ... args) deklariert werden sollte. cmd und args sind die einzigen Parameter, die von außen zugesteuert werden können und völlig unabhängig sind. Der Responder ist für die Außenwelt uninteressant, da er ja in meiner Klasse verwaltet wird und daher direkt an die NetConnection gegeben werden kann.
Das Problem: Innerhalb function myCall(cmd:String, ... args):void kann man nicht einfach nc.call(cmd,res,args); aufrufen, weil die restlichen Argumente innerhalb der Funktion in einem Array vorliegen. Das bedeutet, dass nc.call(cmd,res,args) ein einziges Objekt, nämlich ein Array, als Argument erhält und nicht wie gewünscht alle Einzelargumente. Natürlich hätte ich auch einfach das NetConnection-Objekt public machen können, aber das wäre nicht gerade im Sinn der Kapselung und würde zudem auch die Veröffentlichung des Responders erfordern, der ja an den eigentlichen call übergeben werden muss.
Die Lösung: man darf die gewünschte Funktion, in diesem Fall NetConnection.call, nicht direkt aufrufen, sondern muss sie als Funktionsobjekt behandeln und die Methode apply auf ihm aufrufen. apply akzeptiert bzw. verlangt alle Funktionsparameter in Form eines Arrays, was für die restlichen Argumente ja schon zutrifft. Die beiden anderen fest deklarierten Parameter muss man allerdings auch in dieses Array einfügen, da hier eben alles in einem Array liegen muss.
class MyConnector
{
private nc:NetConnection;
private res:Responder;
...
function call(cmd:String,... args)
{
nc.call.apply(nc,new Array(cmd, res).concat(args));
}
...
}
So… und wenn diesen Beitrag noch jemand versteht: Hut ab! 