自分が何をしているのか、そしていくつかの注意点を正確に理解している限り、あなたのアプローチには本質的に何も悪いことはありません。それは完全に有効なアプローチです。
重複した機能
このアプローチを行うときの私にとっての最大の懸念の1つは、このクラスのインスタンスを新しくするたびに、新しい関数を作成しているということです。1000個のインスタンスを作成すると、1000個の関数が浮かんでいます。メソッドとして、安定したコンテキストで実行される参照インスタンスは1つだけです。
これに対処するための可能な方法は、コンストラクターの外部で静的関数定義を使用することです。
public class SomeClass {
public var toDo:Function;
private static var doIt1:Function = function():void { };
private static var doIt2:Function = function():void { };
public function SomeClass(options:Boolean) {
this.toDo = (option) ? doIt1 : doIt2;
}
}
または、関数ではなくインスタンスメソッドへの参照を使用できます。
public class SomeClass {
public var toDo:Function;
private function doIt1():void { };
private function doIt2():void { };
public function SomeClass(options:Boolean) {
this.toDo = (option) ? doIt1 : doIt2;
}
}
これら2つのアプローチのより良い区別については、次のセクションを参照してください。
いずれにせよ、これは、このクラスのインスタンスの数に関係なく、単一の関数のみを割り当てます。
バインドされた関数とバインドされていない関数
メソッドと関数の間には、ActionScriptには微妙な点があります。似ていますが、まったく同じようには機能しません。バインドされたメソッドは、インスタンスから抽出されたメソッドです。これらのメソッドを実行している間、this
は常にそれらが由来するインスタンスを指し示します。
次のコードを検討してください。
public class SomeClass {
private var number:Number;
public function SomeClass(val:Number):void {
number = val;
}
public var doIt:Function;
public var getNumberUnbound:Function = function():Number {
return this.number;
}
public function getNumberBound():Number {
return this.number;
}
}
非常に単純な例では、コンストラクターがを取り、Number
その値を返す1つのメソッドと、その値も返す1つの関数があり、次に、doIt
現在割り当てられていないという呼び出されたパブリック関数があります。それで、最初にこれを考えてみましょう:
var instance1:SomeClass = new SomeClass(1);
instance1.getNumberBound(); // 1
instance1.getNumberUnbound(); // 1
1
これは、両方の呼び出しで予想されるとおりに戻ります。しかし、今度は、トリッキーになって、.call()
–またはで遊んでみましょう.apply()
。
var instance1:SomeClass = new SomeClass(1);
var instance2:SomeClass = new SomeClass(2);
instance1.getNumberBound.call(instance2); // 1
instance1.getNumberUnbound.call(instance2); // 2
今回はさまざまな結果が得られることに注意してください。バインドされた呼び出しは1
、まだスタックしているため、引き続き返されますinstance1
。バインドされていない呼び出しが返されると、バインドされた呼び出しは2
、本来あるべきと思われるコンテキストで実行されます。実際、次のことを考慮してください。
instance1.getNumberBound.call(null); // 1;
instance1.getNumberUnbound.call(null); // ERROR!
この場合、再びgetNumberBound
はのコンテキストで実行されますがinstance1
、unbound関数は失敗します。これは、がにthis
等しくnull
、null.number
明らかに意味がないためです。
今、さらに奇妙になりましょう:
var instance1:SomeClass = new SomeClass(1);
var instance2:SomeClass = new SomeClass(2);
instance2.doIt = instance1.getNumberBound;
instance2.doIt(); // 1
instance2.doIt = instance1.getNumberUnbound
instance2.doIt(); // 2
getNumberBound
ここでは、から切り離すために何をしようとしても、それができないことがわかりますinstance1
。メソッドへの参照をに押し込みましたinstance2
が、それでも。のスコープ内で実行されますinstance1
。
つまり、私が言うとき、this
それはあなたが期待するものではないかもしれないということです。バインドされているかバインドされていないか、または呼び出し元が.call()
または.apply()
を使用してこれを明示的に変更するかどうかによって、this
予想とは大きく異なる場合があります。
私の本能
私はoccamのかみそりを使用します。これは、オプションを保存し、と呼ばれる方法でスイッチを実行しますtoDo
。この関数を1トンと呼んでも、1つの条件ステートメントのコストはごくわずかであり、パフォーマンスの問題に対処するためのより良い方法を見つける前に、プロファイラーから時間がかかりすぎると言われるのを待ちます。おそらく、パフォーマンスの問題は他の場所にあります。
そうは言っても、私はそれが理にかなっているときにこのパターンを使用します。実際、ワーカーをインスタンスに注入するだけで、条件をすべて一緒に回避できます。
public class SomeClass {
public var toDo:Function;
public SomeClass(doIt:Function) {
toDo = doIt;
}
}
一度コツをつかめば、ActionScriptの関数型プログラミングの概念を理解することは非常に強力なことです。私はそれを避けませんが、注意して使用します。最も強力なものと同様に、誤って使用すると非常に危険な場合があります。
継承は機能するかもしれませんが、「継承はすべての解決策です」に陥らないようにしてください。継承は、実際には「isa」関係をモデル化しようとするだけです。構成は構造的に優れているかもしれませんが、機能的なアプローチについても議論することができます。