Search code examples
actionscript-3oopstructureorganizationcode-organization

AS3 OOP Principal, Structure & Organization


Refining my OOP structure, and trying to make the display of code as intuitive as possible, using basic OOP principals this definitely helps but my craft has only gone so far. I am looking to improve upon my practice:

package
{
import flash.display.*;
import flash.text.*;

public class Button extends Sprite
{   
    protected var _spr:Sprite    = new Sprite();
    protected var _tf :TextField = new TextField();

    protected var c   :Number;
    protected var xx  :Number;
    protected var yy  :Number;
    protected var w   :Number;
    protected var h   :Number;
    protected var t   :String;
    protected var a   :Number;
    protected var l   :Number;
    protected var lC  :Number;

    function Button
        (
        _col:Number,                                          //beginFill
        _x:Number, _y:Number, _w:Number, _h:Number,           //drawRect
        _t:String = "",                                       //TextField (optional)
        _a:Number = 1,                                        //beginFill (optional)
        _l:Number = 0, _lC:Number = 0xFFFFFF                  //lineStyle (optional)
        )
    {
        c      = _col;
        xx     = _x;
        yy     = _y;
        w      = _w;
        h      = _h;
        t      = _t;
        a      = _a;
        l      = _l;
        lC     = _lC;

        _spr.addChild(_tf);

        draw();
    }

    public function draw ():void
    {
        _spr.graphics.clear    ();
        _spr.graphics.lineStyle(l, lC);
        _spr.graphics.beginFill(c);
        _spr.graphics.drawRect (xx, yy, w, h);

            var f:TextFormat = new TextFormat;
                f.font = "Arial";

            _tf.text     = t;
            _tf.autoSize = TextFieldAutoSize.LEFT;
            _tf.x        = xx + w/2 - _tf.textWidth  / 2;
            _tf.y        = yy + h/2 - _tf.textHeight / 1.5;
            _tf.width    = w - 2;
            _tf.height   = h - 2;
            _tf.alpha    = 0.75;

            _tf.setTextFormat(f);

            _tf.selectable   = false;
            _tf.mouseEnabled = false;

        _spr.graphics.endFill ();

    }

    /* ----------------------- *
     *         GETTERS         *
     * ----------------------- */

    //OVERRIDE

    override public function get x      () :Number {return (xx)}
    override public function get y      () :Number {return (yy)}
    override public function get width  () :Number {return (w)}
    override public function get height () :Number {return (h)}

    //SUPPLEMENTARY

    public function get col             () :Number {return (c)}
    public function get text            () :String {return (t)}
    public function get line            () :Number {return (l)}
    public function get lineC           () :Number {return (lC)}

    public function get getObj          () :Sprite {return (_spr)}

    /* ----------------------- *
     *         SETTERS         *
     * ----------------------- */

    //OVERRIDE

    override public function set x (_n:Number) :void
    { xx = getObj.x = Math.round(_n - xx) }

    override public function set y (_n:Number) :void
    { yy = getObj.y = Math.round(_n - yy) }

    override public function set width (_n:Number) :void
    { w  = Math.round(_n) }

    override public function set height (_n:Number) :void
    { h  = Math.round(_n) }

    //SUPPLEMENTARY

    public function set col    (_n:Number) :void
    {
        c = _n;
        draw();
    }

    public function set text   (_s:String) :void
    {
        t = _s;
        draw();
    }

    public function set line   (_n:Number) :void
    {
        l = _n;
        draw();
    }

    public function set lineC(_n:Number) :void
    {
        lC = _n;
        draw();
    }
}
}

From the above Button class, what interpretations can you make about my structure & organization? I have read and followed many articles on how you should perform OOP logically and I think I am ready for some tuning, or criticism rather on my understanding of how a class should be in AS3 (circumstantial i know.)

Some specific questions:

  1. When I instantiate 'Button' from my Main class, should I add it to the stage there or within the Button Class itself using Super?

  2. Will the "1047" Error ever be fixed? What is a sustainable (or more efficient) work-around besides the ones already mentioned here?: http://curtismorley.com/2008/10/15/actionscript-error-1047-parameter-initializer-unknown-or-is-not-a-compile-time-constant/

  3. In order to update width/height changes, I must call my draw function after modification from outside the Override function for width & height specifically. Is there a proper way to call draw() through the Override functions? No error output was provided, it seems doing so is against the rules though so I am just calling draw() from Main as a hack fix.. Possibly write a function within button tethered to event of a changed property?

If anyone bothered to parse this wall of text, thanks for reading and I appreciate any criticism you may have, harsh or otherwise :)


Solution

  • First, your variable names are not decriptive. If you're going to name your variables "c", "t", "lc", etc., at least put inline comments that describe what they're for. Even if you comment the parameters that populate them lower down, there's unnecessary overhead with what you've done.

    If you've done that because of the historically poor code completion in the IDE, use Flash Builder or one of the excellent code editing tools that can help you type out lineColor, textField, etc.

    On closer inspection, it looks like you've done this at least partially because you've reversed the normal convention, which is to have parameters to functions not use underscores, whereas private/protected storage should use underscores, but otherwise should have the same name as the public property. Changing your conventions to match the rest of the AS3 world would go a long way to making your code more readable.

    If you don't want to lose the underscores in your function parameters, you could use a snippet in FB to make setting the instance variable to a parameter of the same name.

    Invalidation is very well understood in ActionScript, so I'm not sure why you have an issue. It looks something like this:

    protected function invalidate():void {
      _isInvalid = true;
      addEventListener(Event.ENTER_FRAME, validate);
    }
    
    protected function validate(e:Event):void {
      removeEventListener(Event.ENTER_FRAME, validate);
      if (_isInvalid) {
        draw();
      }
      _isInvalid = false;
    }
    

    Call this everywhere you're now calling draw(), and it will reduce the number of times you have to update the display list.

    MPO is that you should not have constructor arguments for View Classes, as this makes it impossible to use them from the timeline.

    You'll probably find that if you're open to using the timeline, a lot of this hard work goes away--you simply add the behavior via an AS Class and draw the graphics in the IDE (using things like 9-slice scaling, filters, etc., to minimize maintenance hassle). If you want a button that looks different, you draw a different one, attach your behavior Class, and done.

    A lot of people don't realize that it's possible to maintain good OOP while allowing the timeline to take over much of the heavy lifting. Hope this gives you what you want, or at least gives you some search terms you can move forward with. Note if you're interested in knowing more about leveraging the timeline, post back. The links to the source code on that one were eaten when the post was archived.