2

我没有设计许多在其中一个函数(例如一行)中只有少量差异的类,而是根据构造函数参数更改函数。像这样的东西:

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 个不同功能的两个不同的类似乎是一种浪费。或者我可以在函数中存储选项和 if 语句,但是每次运行函数时我都必须检查它,因为它每次都相同。最好的办法是什么?

4

2 回答 2

3

你的方法本质上没有任何问题——这是一种完全有效的方法——只要你准确地理解你在做什么,以及一些警告是什么。

重复的功能

使用这种方法时,我最大的担忧之一是每次新建此类的实例时,都会创建一个新函数。制作 1000 个实例,你就有 1000 个函数浮动。作为方法,只有一个引用实例在稳定的上下文中执行。

解决此问题的一种可能方法是在构造函数之外使用静态函数定义:

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;
    }
}

请参阅下一节以更好地区分这两种方法。

不管怎样,这只会分配一个函数,不管你有多少这个类的实例。

绑定与未绑定函数

在 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,然后有一个返回该值的方法和一个也返回该值的函数,然后调用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!

在这种情况下,againgetNumberBound仍然在 的上下文中执行instance1,但是未绑定的函数失败了,因为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

在这里您可以看到,无论您尝试做什么来脱离getNumberBoundinstance1您都做不到。即使我们将一个方法的引用塞进了instance2,它仍然在 . 的范围内执行instance1

所以这就是我的意思,当我说this可能不是你所期望的那样。取决于它是绑定还是未绑定,或者调用者是否使用.call()or显式更改它.apply()this可能与您预期的完全不同。

我的本能

我会使用 occam 的剃须刀,它对我来说是存储选项并在名为toDo. 即使您大量调用此函数,单个条件语句的成本也可以忽略不计,我会等待分析器告诉我在尝试找出解决任何性能问题的更好方法之前花费了太长时间。奇怪的是,您的性能问题将在其他地方。

也就是说,当它有意义时,我确实使用了这种模式。事实上,您可以将您的工作人员注入您的实例,并一起避免这种情况。

public class SomeClass {
    public var toDo:Function; 

    public SomeClass(doIt:Function) {
        toDo = doIt;
    }
} 

了解 ActionScript 中的函数式编程概念可能是一件非常强大的事情,一旦您掌握了窍门。我不会避免它,但我会小心使用它。与大多数强大的东西一样,如果使用不当,可能会非常危险。

继承可能有用,但尽量不要陷入“继承是解决一切的办法”。继承应该只真正尝试建模“是”关系,. 组合可能在结构上更好,但也可以为功能方法提出论据。

于 2012-08-04T21:15:03.117 回答
1

在考虑代码时,我会问两个问题:

  • 阅读起来有多容易?
  • 扩展有多容易?

我发现我通常依靠类继承。我发现调试起来很简单,而且(大部分)扩展起来也很简单。如果存在将标志(如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标志作为单独的类会更好,那么您可以扩展Menu1Leveland Menu2Level,并重新编码Menu以适应。

说了这么多,我最初会考虑找到一个免费的菜单系统,甚至付费。它们是正确编程的主要痛苦。由于 Actionscript 与 Javascript 密切相关,您可能会发现可以将 JS 菜单系统直接移植到 AS3 中。

编辑

(更正了声明示例中的一个错误。)

于 2012-08-04T20:09:31.367 回答