0

I'm an actionscript newbie so please bear with me. Below is a function, and I am curious how to set default property values for objects that are being created in a loop.

In the example below, these propeties are the same for each object created in the loop: titleTextField.selectable, titleTextField.wordWrap, titleTextField.x

If you pull these properties out of the loop, they are null because the TextField objects have not been created, but it seems silly to have to set them each time. What is the correct way to do this. Thanks!

var titleTextFormat:TextFormat = new TextFormat();   
    titleTextFormat.size = 10;  
    titleTextFormat.font = "Arial";
    titleTextFormat.color = 0xfff200;

for (var i=0; i<arrThumbPicList.length; i++) {

    var yPos = 55 * i

    var titleTextField:TextField = new TextField();  
        titleTextField.selectable = false;
        titleTextField.wordWrap = true;
        titleTextField.text = arrThumbTitles[i];
        titleTextField.x = 106;
        titleTextField.y = 331 + yPos;

    container.addChild(titleTextField);
    titleTextField.setTextFormat(titleTextFormat);

}

4 Answers 4

3

There are basically three options here.

  1. You could create a custom class that acts as a proxy to a TextFormat. TextFormatProxy, for instance, which could create a TextFormat in it's constructor and set each of your default values. See this link or google "AS3 proxy pattern."
  2. You could write a custom class that extends TextFormat and likewise, set these default values in the constructor, or in a function of your choosing.
  3. You could use a little helper function like this

package {
    import flash.display.Sprite;

    public class DefaultProperties extends Sprite{

        public var s:Sprite;

        public function DefaultProperties() {
            s = new Sprite();
            s.graphics.beginFill(0x00ff00, 1);
            s.graphics.drawRect(0, 0, 100, 100);
            s.graphics.endFill();
            this.setDefaults(s, {x:100, y:200, scaleX:.5});
            this.addChild(s);
       }

       function setDefaults($obj:*, $properties:Object):void {
           for (var i in $properties) {
               $obj[i] = $properties[i];
           }
       }
    }
}
Sign up to request clarification or add additional context in comments.

Comments

2

Without knowing the surrounding code, I'd suggest putting any construction logic in a factory object. If that's overkill, then I'd say your code is fine. Pulling it out of the loop and into a helper method is really just adding another layer of indirection which is not only harder to read, but it's also less efficient since you're introducing another method call on the stack.

Using a factory object is good if your class is really not interested in how things are created, but rather just needs one or more instance of something. That way, the factory is responsible for the creation of the object and you get a nice separation of concerns.

I wouldn't recommend creating a proxy structure that only has the purpose of setting the properties of your instance. I think it's an anti pattern because it favors inheritance over composition and it does so solely for the purpose of code re-use. But doing this means you're locking yourself to that specific implementation, which is likely not what you want. Just because your textfield has certain value, doesn't mean that it is a different type.

This is how your code could look when using the factory pattern, first out, the format factory:

public interface FormatFactory
{
    function getInstance():TextFormat;
}

public class TitleFormatFactory implements FormatFactory
{
    public function getInstance():TextFormat
    {
        var format:TextFormat = new TextFormat();   
            format.size = 10;  
            format.font = "Arial";
            format.color = 0xfff200;

        return format;
    }
}

Factories may or may not be parameterized:

public interface TextFieldFactory
{
    function getInstance(text:String, position:Point, format:TextFormat):TextField;
}

public class TitleFactory implements TextFieldFactory
{
    public function getInstance(text:String, position:Point, format:TextFormat):TextField
    {
        var title:TextField = new TextField();  
            title.selectable = false;
            title.wordWrap = true;
            title.text = text;
            title.x = position.x;
            title.y = position.y;
            title.setTextFormat(format);

        return title;
    }
}

Lastly, this is how you'd use the code:

var formatFactory:FormatFactory = new TitleFormatFactory();
var titleFactory:TextFieldFactory = new TitleFactory();

var format:TextFormat = formatFactory.getInstance();

for (var i = 0; i < arrThumbPicList.length; i++)
{
    var position:Point = new Point(106, 331 + 55 * i);
    var title:TextField = titleFactory.getInstance(text, position, format);

    container.addChild(title);
}

Besides being readable, a huge benefit is that you can now swap the implementations of the factories and thus changing what kind of components you're using, without having to change your actual logic. All you have to do is change the references to the factories.

Also, by separating the concerns you make it easier to focus on one aspect of your code and thus run less of a risk of introducing errors and if you still do, those are often easier to spot and fix. More-over, it's much easier to unit test code when you separate concerns and more importantly, creation logic from business logic.

2 Comments

Awesome answer, thanks for taking the time to write it. Very cool design pattern. -Paul
You're quite welcome. The factory pattern is probably one of the most frequently used design patterns out there. It's very simple to grok, requires little boilerplate and enables you to cleanly separate creation logic from business logic. Don't forget to mark accept an answer btw! :)
0

If I were to approach the same problem I'd use the same as you posted, just because making the new data structure would be unnecessary. An alternative would be to create a function that accepts the textbox as a parameter:

for (var i=0; i<arrThumbPicList.length; i++) {
     var titleTextField:TextField = new TextField();
     setProperties(titleTextField,i);
}


function setProperties(txt:TextField,offset:int){
       var yPos = 55 * offset;
       txt.selectable = false;
       txt.wordWrap = true;
       txt.text = arrThumbTitles[offset];
       txt.x = 106;
       txt.y = 331 + yPos;

       container.addChild(titleTextField);
       titleTextField.setTextFormat(titleTextFormat);

       i++;
  }

Although I'd probably go with the way you posted unless you're adding things from multiple for loops.

Comments

0

Your code is ok for me. Simple and effective. You only have 3 fixed parameters, for me it's not necessary to do more :

titleTextField.selectable = false;
titleTextField.wordWrap = true;
titleTextField.x = 106;

Unlike the other answer, I would do a helper function to get the textfield with all fixed parameters (I would do that ONLY if there was a LOT of stuff to do).

function createMyContextTextField():TextField{
    textfield:TextField = new textField();
    textfield.selectable = false;
    textfield.wordWrap = true;
    textfield.x = 106;
    return textfield;
}

Comments

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.