1
\$\begingroup\$

I am doing a game that uses nape physics in ActionScript, but my question isn't about language. It is about the use of patterns.

I had fruits in my game. I did one interface Fruit and fruits like Banana using this interface. I also made a class Fruits to get random fruits in runtime.

public interface WorldElement {
    function get body():Body;
}

public interface Fruit extends WorldElement {

    function getName():String;

    function getPoint():Number;

    function getShieldTimeIncrement():Number;

    function getSpeed():Number;

}

public class Banana implements Fruit {

    public function Banana() {
        this._body = WorldBodies.createBody("fr_banana");
        this._body.allowRotation = false;
        this._body.type = BodyType.KINEMATIC;
    }

    private var _body:Body;

    public function get body():Body {
        return _body;
    }

    public function getName():String {
        return "Banana";
    }

    public function getPoint():Number {
        return 10;
    }

    public function getShieldTimeIncrement():Number {
        return 2;
    }

    public function getSpeed():Number {
        return 1;
    }
}

internal class Fruits {

    public function Fruits() {

    }

    public static function createNewRandomFruit():Fruit {

        const randomNumber:int = int(Math.random()*100);

        if(randomNumber <= 10)
        {
            return new Kwiui();
        }

        if(randomNumber <= 25)
        {
            return new Watermelon();
        }

        if(randomNumber <= 40)
        {
            return new Pineapple();
        }

        if(randomNumber <= 60)
        {
            return new Orange();
        }

        return new FruitBanana();
    }
}

That is my first implementation, but I thought in refactor because I had any fruits and repetitive implementation. I believed the Builder help me, and I do refactor in code.

This is my refactored code, an abstract class builder, and my product fruit and my variations builder to specify fruit like banana.

internal class AFruitBuilder {

    protected var fruit:Fruit = new Fruit();
    internal function configFruitName():void {}
    internal function configPoint():void {}
    internal function configSpeed():void {}
    internal function configIfIsActiveShield():void {}
    internal function configTimeToIncrimenetSheld():void {}
    internal function configPhysicsBody():void {}
    internal function getFruit():Fruit
    {
        return fruit;
    }
}

public class FruitBanana extends AFruitBuilder{

    override internal function configFruitName():void {
        fruit.name = "FruitBanana";
    }

    override internal function configPoint():void {
        fruit.point = 10;
    }

    override internal function configSpeed():void {
        fruit.speed = 1;
    }

    override internal function configIfIsActiveShield():void {
        fruit.activeShield = false;
    }

    override internal function configTimeToIncrimenetSheld():void {
        fruit.timeToIncrementShield = 3;
    }

    override internal function configPhysicsBody():void
    {
        fruit.body = WorldBodies.createBody("fr_banana");
        fruit.body.allowRotation = false;
        fruit.body.type = BodyType.KINEMATIC;
    }

}    

This is my abstract builder:

internal class AFruitBuilder {

    protected var fruit:Fruit = new Fruit();
    internal function configFruitName():void {}
    internal function configPoint():void {}
    internal function configSpeed():void {}
    internal function configIfIsActiveShield():void {}
    internal function configTimeToIncrimenetSheld():void {}
    internal function configPhysicsBody():void {}
    internal function getFruit():Fruit
    {
        return fruit;
    }
}

This is my concrete builder extends my abstract, and I have other fruits like watermelon:

public class FruitBanana extends AFruitBuilder{

    override internal function configFruitName():void {
        fruit.name = "FruitBanana";
    }

    override internal function configPoint():void {
        fruit.point = 10;
    }

    override internal function configSpeed():void {
        fruit.speed = 1;
    }

    override internal function configIfIsActiveShield():void {
        fruit.activeShield = false;
    }

    override internal function configTimeToIncrimenetSheld():void {
        fruit.timeToIncrementShield = 3;
    }

    override internal function configPhysicsBody():void
    {
        fruit.body = WorldBodies.createBody("fr_banana");
        fruit.body.allowRotation = false;
        fruit.body.type = BodyType.KINEMATIC;
    }

}

This is my super class in my model. For example, my game has other elements other than fruit.

public class WorldElement {
    public var body:Body;
}

public class Fruit extends WorldElement {

    public var name:String;
    public var point:Number;
    public var speed:Number;
    public var activeShield:Boolean;
    public var timeToIncrementShield:Number;

}

This is my way to process the construction of fruit:

public class FruitMaker {

    protected var fruitBuilder:AFruitBuilder

    public function FruitMaker(fruit:AFruitBuilder) {
        this.fruitBuilder = fruit;
        fruitBuilder.configFruitName();
        fruitBuilder.configIfIsActiveShield();
        fruitBuilder.configPhysicsBody();
        fruitBuilder.configPoint();
        fruitBuilder.configSpeed();
        fruitBuilder.configTimeToIncrimenetSheld();
    }

    public function getFruit():Fruit
    {
        return fruitBuilder.getFruit();
    }
}

internal class Fruits {

    public static function createNewFruitRandom():Fruit {

        const randomNumber:int = int(Math.random()*100);

        if(randomNumber <= 10)
        {
            return new FruitMaker( new FruitBanana() ).getFruit();
        }

        if(randomNumber <= 25)
        {
            return new FruitMaker( new FruitWatermelon() ).getFruit();
        }

        if(randomNumber <= 40)
        {
            return new FruitMaker( new FruitPineapple() ).getFruit();
        }

        if(randomNumber <= 60)
        {
            return new FruitMaker( new FruitOrange() ).getFruit();
        }

        return new FruitMaker( new FruitBanana() ).getFruit();
    }
}

Was using Builder in my case necessary? What is better? How can I resolve my repetitive code?

\$\endgroup\$
2
\$\begingroup\$

The whole point of creating builders is to be able to make objects that have different values. Like "make me a small orange", "make me a big orange", "make me a fast orange", "make me a slow orange". The builder takes the default values of the orange, applies the requested changes to the input, then creates the object.

What you have done is created builders for fixed values. In that case, you're better off applying the Factory Pattern - Like a vending machine, I push a button and I get my object. createNew(? extends Fruit).

To that end, you could have made the following:
- A no-args constructor for each Fruit to set default values
- A all-args constructor for each Fruit to set custom values.

By doing so, you remove the need for the FruitMaker class and the AFruitBuilder. All Fruits can be Fruit again.

Don't worry about repeating yourself too much if this repetition can be created via the IDE by just generating code.

\$\endgroup\$
  • \$\begingroup\$ I agreed with your point, and this make total sense.. thanks a lot for help.. \$\endgroup\$ – ViTUu Jun 1 '15 at 17:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.