Proxy クラス (livedocs@lab) は既存のオブジェクトの振る舞いを変えたいときに使います。例えば、Proxy のサブクラスである ObjectProxy クラス (livedocs@lab) は属性が変更されたとき、イベントでそれを知らせることができるようになっています。(注:ObjectProxy は Flex 2 フレームワークのクラスです)
Proxy クラスにはコンストラクタがありません。従って、Proxy クラスを使用する際は、サブクラスを定義しメソッドをオーバーライドするのが基本です。オーバーライドしていないメソッドが呼ばれた場合は例外が投げられます。
属性へのアクセスの実現
さて、Proxy クラス(のサブクラス)のインスタンスの属性を参照するには getProperty メソッドをオーバーライドします。
import flash.util.*; public dynamic class MyProxy extends Proxy { private var _obj:Object = new Object(); override flash_proxy function getProperty(name:*):* { return _obj[name]; } }
Proxy クラスのメソッドの呼ばれ方は少々変わっています。例えばここで定義している getProperty() メソッドが呼ばれるのは Proxy クラスのサブクラスのインスタンスの属性が参照されたときです。
つまり、Proxy のメソッドは普通に直接呼びだすためのものではありません。これらのメソッドには通常の属性へのアクセスと区別するために別の名前空間(flash_proxy)が指定されています。
実際に MyProxy を使用するコードは以下のようになります。
var myProxy:MyProxy = new MyProxy(); trace(myProxy.foo); // MyProxy の getProperty(foo) が呼ばれる。出力は undfined になる。
コーディング上 MyProxy に定義されていない属性(この例では foo)にアクセスすることになるため、上の MyProxy のクラス定義では dynamic なクラスとして宣言しています。(コンパイラの -strict オプションをオフにしても構いません。)
属性操作には、設定/削除/検査もありますね。これらに対応するメソッドも MyProxy に実装してみます。
override flash_proxy function setProperty(name:*, value:*):void { _obj[name] = value; } override flash_proxy function deleteProperty(name:*):Boolean { return delete _obj[name]; } override flash_proxy function hasProperty(name:*):Boolean { return name in _obj; }
これで、以下のような操作が可能になります。
var myProxy:MyProxy = new MyProxy(); myProxy.foo = "foo"; // setProperty が呼ばれる trace(myProxy.foo); // getProperty が呼ばれる:出力は foo trace("foo" in myProxy); // hasProperty が呼ばれる:出力は true trace(delete myProxy.foo); // deleteProperty が呼ばれる:出力は true
関数の呼び出しの実現
Proxy のサブクラスに対して関数をコールすると callProperty メソッドが呼ばれます。callProperty メソッドの使い方は以下のような感じです。
override flash_proxy function callProperty(name:*, ...rest):* { var func:Function = _obj[name] as Function; if (func != null) { return func.apply(null, rest); } } var myProxy:MyProxy = new MyProxy(); myProxy.bar = function():String { return "bar"; }; trace(myProxy.bar()); // callProperty が呼ばれ、bar が出力される
イテレーションへの対応
for..in や for each..in のようなイテレーションで Proxy を使えるようにするには、まず nextNameIndex() メソッドをオーバーライドします。このメソッドは次の属性のインデックス値を返します。
private var _props:Array; // 属性格納用の配列 override flash_proxy function nextNameIndex(index:int):int { if (index == 0) { // イテレーションの開始、配列を初期化する _props = new Array(); for (var:* x in _obj) { _props.push (x); } } if (index < _props.length) { return index + 1; } else { return 0; } }
for..in を使用するには nextName() メソッドもオーバーライドします。
override flash_proxy function nextName(index:int):String { return _props[index-1].toString();; // nextNameIndex が 1 から返すため } for (var prop:* in myProxy) { trace(prop + " = " + myProxy[prop]); }
for each ..in を使用するには nextValue() メソッドをオーバーライドします。
override flash_proxy function nextValue(index:int):* { return _obj[_props[index-1]]; } for each (var item:* in myProxy) { trace(item); }