2

関数の1つ(1行など)が少しだけ異なる多くのクラスを設計する代わりに、コンストラクター引数に基づいて関数を変更してきました。このようなもの:

public class SomeClass {
    public var toDo : Function;

    public SomeClass1( option: Boolean){
        if (option){
            function function1():void{
                // something here;
            }
            toDo = function1;

        } else{
            function function2():void{
                // something here;
            }
            toDo = function2;
        }
    }
}

これは良い習慣ですか?あるいは、基本クラスと2つのサブクラスに分けることもできますが、わずかな違いで2つの異なる機能を持つ2つの異なるクラスとしてそれらを使用するのは無駄に思えます。または、オプションを格納して関数にifステートメントを含めることもできますが、関数を実行するたびに、毎回同じである場合はそれをチェックする必要があります。何をするのが最善でしょうか?

4

2 に答える 2

3

自分が何をしているのか、そしていくつかの注意点を正確に理解している限り、あなたのアプローチには本質的に何も悪いことはありません。それは完全に有効なアプローチです。

重複した機能

このアプローチを行うときの私にとっての最大の懸念の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等しくnullnull.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」関係をモデル化しようとするだけです。構成は構造的に優れているかもしれませんが、機能的なアプローチについても議論することができます。

于 2012-08-04T21:15:03.117 に答える
1

コードについて考えるときに私が尋ねる2つの質問があります:

  • 読むのはどれくらい簡単ですか?
  • 延長するのはどれくらい簡単ですか?

私は通常、クラスの継承に頼っています。デバッグは簡単で、(ほとんど)拡張も簡単だと思います。フラグ(のような)をメソッドまたは別の子構造に変更する問題がある場合willProcessConfirmKey、それは単純です(大量のテストが必要な場合でも)。また、多くのクラスをより小さなクラスのグループに折りたたむのも簡単です。

あなたのコメントから、私はそれがこのように設定されているかもしれないと思います:

import flash.display.Sprite;
import flash.events.KeyboardEvent;

public class Menu extends Sprite
{
    protected var keys:Array;
    protected var _willProcessConfirmKey:Boolean;

    public function Menu(willProcessConfirmKey:Boolean)
    {
        super();

        this.willProcessConfirmKey = willProcessConfirmKey;

        addEventListener(KeyboardEvent.KEY_DOWN, keyHnd, false, 0, true);
    }

    private function keyHnd(e:KeyboardEvent):void
    {
        storeKeys();
        processKeys();
    }

    private function storeKeys():void
    {
        // store keys in some fashion here
    }

    protected function processKeys():void
    {
        // this function is provided for overriding.
    }

    public function get willProcessConfirmKey():Boolean 
    {
        return _willProcessConfirmKey;
    }

    public function set willProcessConfirmKey(value:Boolean):void 
    {
        _willProcessConfirmKey = value;
    }
}

public class Menu1Level extends Menu
{
    public function Menu1Level(willProcessConfirmKey:Boolean)
    {
        super(willProcessConfirmKey);
    }

    override protected function processKeys():void
    {
        super.processKeys();

        // handle movement and selection of items here.
        if (willProcessConfirmKey) {
            // process confirms
        } else {
            // skip processing confirms.
        }
    }
}

public class Menu2Level extends Menu
{
    public function Menu2Level(willProcessConfirmKey:Boolean)
    {
        super(willProcessConfirmKey);
    }

    override protected function processKeys():void
    {
        super.processKeys();

        // handle movement and selection of items here.
        if (willProcessConfirmKey) {
            // process confirms
        } else {
            // skip processing confirms.
        }
    }
}

そして、これを次のように使用します。

public var menu1Level_NoConfirm:Menu1Level = new Menu1Level(false);
public var menu2Level_Confirm:Menu2Level = new Menu2Level(true);

willProcessConfirmKeyフラグが別のクラスとして優れていることがわかった場合は、拡張Menu1LevelしてMenu2Level、に対応するように再コーディングできMenuます。

そうは言っても、私は最初に無料のメニューシステムを見つけること、あるいはそれを支払うことさえ考えていました。それらは正しくプログラムするための大きな苦痛です。ActionscriptはJavascriptと非常に密接に関連しているため、JSメニューシステムを直接AS3に移植できる場合があります。

編集

(宣言例の誤りを訂正しました。)

于 2012-08-04T20:09:31.367 に答える