2

Instead of designing a lot of classes that differ by only a small amount in one of their functions (like, one line), I've been changing the function based on a constructor arguments. Something like this:

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

Is this good practice? Alternatively, I could separate it into a base class, and 2 sub classes, but it seems such a waste to have them as two different classes with 2 different functions when they differ by so little. Or I could store option and have if statements in the function, but then I have to check it every time I run the function when it's the same every time. What would be the best thing to do?

3
  • 1
    One thing to note about function literals, they are unbound -- unlike class methods -- so this might not be what you are expecting it to be. Commented Aug 4, 2012 at 14:54
  • Also be aware that each instance of the class will have distict references to a function literal. Commented Aug 4, 2012 at 14:56
  • What do you mean it might not be what I'm expecting? In this example, is toDo still a method of SomeClass? Commented Aug 4, 2012 at 15:53

2 Answers 2

3

There is nothing inherently wrong with your approach – it is a totally valid approach – as long as you understand exactly what you are doing, and what some of caveats are.

Duplicated functions

One of the largest concerns for me when doing this approach is that every time you new up an instance of this class, you are creating a new function. Make 1000 instances, you have 1000 functions floating around. Where as methods, there is only one reference instance that gets exectued in a stable context.

A possible way to address this is to use a static function definition, outside of the constructor:

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

Or, you could use references to instance methods rather than functions:

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

See the next section for a better distinction between these two approaches.

Either way though, this will only allocate a single function, no matter how many instances you have of this class.

Bound vs. Unbound functions

There is a subtlety in ActionScript between methods and functions. Although similar, they don't work quite the same way. A bound method is a method that has been extracted from an instance. While executing these methods, this will always point at the instance that they came from.

Consider the following code:

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

Pretty simple example, the constructor takes a Number and then there is one method that returns that value and one function that also returns that value, and then a public function called doIt that is currently unassigned. So first lets consider this:

var instance1:SomeClass = new SomeClass(1);
instance1.getNumberBound(); // 1
instance1.getNumberUnbound(); // 1

This returns 1 as we would expect for both calls. But now, lets try to get tricky and play around with .call() – or .apply().

var instance1:SomeClass = new SomeClass(1);
var instance2:SomeClass = new SomeClass(2);
instance1.getNumberBound.call(instance2); // 1
instance1.getNumberUnbound.call(instance2); // 2

Notice this time we get different resules. the bound call still returns 1 because its still stuck to instance1, where as the unbound call returns 2, cause it is executed in whatever context it thinks it should be in. In fact, consider this:

instance1.getNumberBound.call(null); // 1;
instance1.getNumberUnbound.call(null); // ERROR!

In this case, again getNumberBound is still executed in the context of instance1, however the unbound function fails, because this now equals null, and null.number obviously doesn't make any sense.

Now lets get even weirder:

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

Here you can see that no matter what you try to do to detach getNumberBound from instance1, you just can't do it. Even though we have shoved a reference to a method into instance2, its still executed within the scope of instance1.

So that is what I mean, when I say this might not be what you expect it to be. Depending on if its bound or unbound, or if the caller explicitly changes this using .call() or .apply(), this can be something wildly different than you expect.

My Instinct

I would use occam's razor, which to me is storing option and doing your switch in a method called toDo. Even if you are calling this function a ton, the cost of a single conditional statement is neglible, and I would wait for the profiler to tell me that it was taking too long before trying to figure out a better way to address any performance problem. Odds are, your performance problems will lie elsewhere.

That said, I do use this pattern, when it make sense. In fact, you can just inject your worker into your instance, and avoid the condition all together.

public class SomeClass {
    public var toDo:Function; 

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

Understanding functional programming concepts in ActionScript can be a very powerful thing, once you get the hang of it. I wouldn't avoid it, but I would use it with care. As with most powerful things, it can be very dangerous if wielded incorrectly.

Inheritance might work, but try not to fall into the "Inheritance is the solution to everything". Inheritance should only really try to model a "is a" relationship, . Composition might be structurally better, but one could make the argument for a functional approach as well.

Sign up to request clarification or add additional context in comments.

1 Comment

@32bitkid, nice thorough explanation. I would like to comment on the conclusion, though. From @Sanchez157643's answer's comments, it's clear that there will be some specialized methods for each menu item. A switch would organize the code, but also put unnecessary and unused code in some -- if not all -- menu instances. As for myself, I try to avoid anything relating to function programming, if possible. It is far too confusing to debug, and I find most of my life is spent in debugging. I rely on . . . ok, hope that . . . Adobe can design the bytecode implementation of inheritance well.
1

There are two questions I ask when thinking about code:

  • How easy will it be to read?
  • How easy will it be to extend?

I find that I usually fall back on class inheritance. I find it simple to debug, and simple (mostly) to extend. If there is a question of changing a flag (like willProcessConfirmKey) into a method or a separate child structure, it is simplistic (even if it takes a great deal of testing). It is also simple to collapse a lot of classes back into a smaller group of classes.

From your comments, I think it might be set up something like this:

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

And use this like so:

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

If you find that the willProcessConfirmKey flag will be better as a separate class, then you can extend Menu1Level and Menu2Level, and recode Menu to accommodate.

All this being said, I would initially think about finding a free menu system, or even paying for one. They are a major pain to program correctly. Since Actionscript is very closely related to Javascript, you may find that you can port a JS menu system directly into AS3.

Edit

(Corrected a mistake in the declaration example.)

2 Comments

Thanks. Is there really any benefit to declaring a variable of to be of a certain type and then instantiating it as a subclass like you have there for menu2Level_Confirm? It just makes it so that you can't access any public methods of the subclass, not that we have any in this example. That is, why didn't you write var menu2Level_Confirm:Menu2Level = new Menu2Level(true); or public var menu1Level_NoConfirm:Menu = new Menu1Level(false); public var menu2Level_Confirm:Menu = new Menu2Level(true); instead?
The disadvantage is creating a more processor intensive run-time assignment using var mn:Menu = new Menu2Level(true); versus the compile-time assignment for instantiating the class itself. It's not a problem to cast a super as a sub (if it is a sub, e.g., Menu2Level(mn)), except that this is also a run-time conversion. You have to understand why a method is in a sub or super class. Certain methods are shared between sub and super (processKeys), and others only exist to the super class (storeKeys), and still others are defined in one and only one sub class (maybe showMenuGrid?).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.