Hot File

Why Bother Using It and What's a Configuration Object

View: 204    Dowload: 0   Comment: 0   Post by: hanhga  
Author: none   Category: Flash template, Flash game   Fields: Other

10 point/3 review File has been tested

It's a pain to have to change the parameters of a function; you have to change every other call to that function in order to avoid errors.

Introduction

It's a pain to have to change the parameters of a function; you have to change every other call to that function in order to avoid errors.

But you can get around this by using just one parameter: a configuration object.

Here's a silly example of a function for creating a robot:

function generateRobot(arms:int, personality:String):Robot {
    var robot:Robot = new Robot();
     
    for (var i:int = 0; i < arms; i++) {
        //create arm and add it to robot
    }
 
    if (personality == "evil") {
        robot.commands = "Destroy mankind.";
    }
    else {
        robot.commands = "Bake cookies."
    }
    return robot;
}
 
generateRobot(2, "evil");

Now, here's the same example, using a configuration object:

function generateRobot(conf:Object):Robot {
    var robot:Robot = new Robot();
     
    for (var i:int = 0; i < conf.arms; i++) {
        //create arm and add it to robot
    }
 
    if (conf.personality == "evil") {
        robot.commands = "Destroy mankind.";
    }
    else {
        robot.commands = "Bake cookies."
    }
    return robot;
}
 
generateRobot({arms:2, personality:"evil"});

So if there's hardly any difference, why would we bother doing it the second way? After all, it actually makes the function a little harder to use; whereas before our IDE would be able to give us this information about the parameters the function expected:

...now it can only give us this:

Suppose you want to add a couple more parameters: one specifying the material to use and another to specify what color its laser should be. That's not too hard, in either case:

function generateRobot(arms:int, personality:String, material:String, laserColor:String):Robot {
    var robot:Robot = new Robot();
     
    for (var i:int = 0; i < arms; i++) {
        //create arm and add it to robot
    }
 
    if (personality == "evil") {
        robot.commands = "Destroy mankind.";
    }
    else {
        robot.commands = "Bake cookies."
    }
 
    switch (material) {
        case "wood":
            //wooden robot
        break;
        case "steel":
        default:
            //steel robot
        break;
    }
 
    robot.laser = new Laser();
    robot.laser.color = laserColor;
 
    return robot;
}
 
generateRobot(2, "evil", "steel", "red");
function generateRobot(conf:Object):Robot {
    var robot:Robot = new Robot();
     
    for (var i:int = 0; i < conf.arms; i++) {
        //create arm and add it to robot
    }
 
    if (conf.personality == "evil") {
        robot.commands = "Destroy mankind.";
    }
    else {
        robot.commands = "Bake cookies."
    }
 
    switch (conf.material) {
        case "wood":
            //wooden robot
        break;
        case "steel":
        default:
            //steel robot
        break;
    }
 
    robot.laser = new Laser();
    robot.laser.color = conf.laserColor;
 
    return robot;
}
 
generateRobot({arms:2, personality:"evil", material:"steel", laserColor:"red"});

So far, still not much of a difference. What if you want your robots to all have red lasers by default? Simple again. Without a configuration object, you just need to change the method signature (the function line), and then you can remove the last argument from the function call:

function generateRobot(arms:int, personality:String, material:String, laserColor:String = "red"):Robot {
    //this is all the same
}
 
generateRobot(2, true, "steel");    //I removed the last argument

With a configuration object, it's a little trickier - though not much:

function generateRobot(conf:Object):Robot {
    if (!conf.laserColor) {
        conf.laserColor = "red";
    }
 
    var robot:Robot = new Robot();
     
    for (var i:int = 0; i < conf.arms; i++) {
        //create arm and add it to robot
    }
 
    if (conf.personality == "evil") {
        robot.commands = "Destroy mankind.";
    }
    else {
        robot.commands = "Bake cookies."
    }
 
    switch (conf.material) {
        case "wood":
            //wooden robot
        break;
        case "steel":
        default:
            //steel robot
        break;
    }
 
    robot.laser = new Laser();
    robot.laser.color = conf.laserColor;
 
    return robot;
}
 
generateRobot({arms:2, personality:"evil", material:"steel"});   //I removed the last argument

Okay. Now suppose you find that you're setting almost all of your robots to be evil (I mean, why not?), so it's actually kind of a pain to write "evil" as a parameter every time. Naturally, you want to set "evil" as the default - but you don't want to set a default material.

The only way you can do this, with a regular set of function parameters, is to switch the order of the personality and material parameters:

function generateRobot(arms:int, material:String, personality:String = "evil", laserColor:String = "red"):Robot {

Ah, but now you have to switch the order of the arguments round on every single function call!

generateRobot(2, "evil", "steel");    //no longer works

A configuration object doesn't give you this problem. Check it out:

function generateRobot(conf:Object):Robot {
    if (!conf.laserColor) {
        conf.laserColor = "red";
    }
    if (!conf.personality) {
        conf.personality = "evil"
    }
 
    //this is all the same
}
 
generateRobot({arms:2, material:"steel"});  //no "personality" parameter? no problem!

Neat! All your old generateRobot() function calls will continue to work, but you can create new calls that don't bother specifying personality.

You can even decide to get rid of the personality parameter altogether:

function generateRobot(conf:Object):Robot {
    if (!conf.laserColor) {
        conf.laserColor = "red";
    }
    if (!conf.personality) {
        conf.personality = "evil"
    }
 
    var robot:Robot = new Robot();
     
    for (var i:int = 0; i < conf.arms; i++) {
        //create arm and add it to robot
    }
 
    robot.commands = "Destroy mankind.";
 
    switch (conf.material) {
        case "wood":
            //wooden robot
        break;
        case "steel":
        default:
            //steel robot
        break;
    }
 
    robot.laser = new Laser();
    robot.laser.color = conf.laserColor;
 
    return robot;
}

The above version of the function doesn't refer to conf.personality at all - but you won't get an error if you still have calls like this:

generateRobot({arms:2, personality:"evil", material:"steel"});

Of course, you might get a few confused users if you have calls like this:

generateRobot({arms:2, personality:"good", material:"steel"});

...since all robots are now evil. But at least the code will compile.

For the same reason, you can change the order of the arguments without it mattering at all, and even add in new parameters that don't do anything yet:

generateRobot({material:"steel", laserColor:"green", arms:2, voice:"Mr. T"});

The code for setting the defaults is easy to understand so far, but is going to be very annoying to extend if we need to have lots of parameters:

if (!conf.laserColor) {
    conf.laserColor = "red";
}
if (!conf.personality) {
    conf.personality = "evil"
}

Let's write some more general code to cope with it:

var defaults:Object = {
    laserColor:red,
    personality: "evil"
}
 
for (var key:String in defaults){
    if (!conf[key]) {
        conf[key] = defaults[key];
    }
}

That for loop may be a little confusing, so I'll break it down. First, look at this:

for (var key:String in defaults){
    trace(key);
}

This is a for...in loop, which will output the names of the keys inside the defaultobject:

laserColor
personality

Next, look at this line:

trace(defaults["laserColor"]);

This will output red - it's the same as writing trace(defaults.laserColor).

Following on from that, look at this example:

var example:Object = { demo: "test" };
trace(example["demo"]);
trace(example["foo"]);

What do you think this will output?

Well, example["demo"] is the same as example.demo, which equals "test". Butexample.foo does not exist, so example["foo"] will return null. This means that!example["foo"] (note the exclamation mark) will be equivalent to true.

Put that all together, and you should be able to understand why this code works:

var defaults:Object = {
    laserColor:red,
    personality: "evil"
}
 
for (var key:String in defaults){
    if (!conf[key]) {
        conf[key] = defaults[key];
    }
}

Give me a shout in the comments if you need a hand!

For an even quicker version, try this:

function generateRobot(conf:Object = null):Robot {
    var conf:Object = conf || {};
    var defaults:Object = {
        laserColor:red,
        personality: "evil"
    }
     
    for (var key:String in defaults){
        conf[key] = conf[key] || defaults[key];
    }

The change in Line 1 (and new Line 2) means that even the conf object itself is optional, so you can just call generateRobot(). (Of course, you'll need to change the code to deal with the values that don't currently have defaults.)

As I mentioned above, the IDE can't give you any tips about what parameters a function is expecting, if that function uses a configuration object. This is a major drawback, as it can make your code really hard to use; you have to remember which parameters go in the conf object, as well as all of their names and types.

But we can still display this information to the coder when it's needed; we just have to do so manually, like so:

/**
 * Generate a robot, based on the parameters given.
 * @param   conf    Configuration object. Expects:
     * arms         (int)       Number of arms robot should have.
     * personality  (String)    Personality of robot. Can be "evil" or "good". Defaults to "evil".
     * material     (String)    What the robot should be made out of. Can be "steel" or "wood" at this time.
     * laserColor   (String)    Color of the robot's laser. Defaults to "red".
     * voice        (String)    Vocal stylings of robot. Currently not implemented.
 * @return  The finished robot.
 */
function generateRobot(conf:Object):Robot {
    //
}

Now, if I start to write a call to this function in FlashDevelop (my IDE of choice), I see this:

Sure, it's a bit of a pain to keep this manually updated, but in many cases it's worth it.

Why Bother Using It and What's a Configuration Object

Why Bother Using It and  What's a Configuration Object Posted on 14-01-2016  It's a pain to have to change the parameters of a function; you have to change every other call to that function in order to avoid errors. 3.3333333333333/10 204

Comment:

To comment you must be logged in members.

Files with category

 
Newsletter Email

File suggestion for you

File top downloads

logo codetitle
Codetitle.com - library source code to share, download the file to the community
Copyright © 2015. All rights reserved. codetitle.com Develope by Vinagon .Ltd