Software Design – State Machine

For a class with different state, instead of using switch-case loop all the time, there should be a state system to manage the behavior for each state.

For example a monster with different attacking state, there should be a class for each state, and a list of the object with those classes to represent states. In this case, each state is an enemy behavior containing a list of enemy behaviors (see also: Software Design: Feature based design on class), which is a flexible pattern to receive enemy event and share behavior between different state. (p.s.: In the other word, a normal enemy is an enemy with one state.)

Fire Dragon: attack
-> Fire Ball State (current Enemy State): receive attack event, board-cast event to its behaviors
-> Fire Ball Behavior (the only behavior in Fire Ball State): receive attack event, generate a fire ball

Sudo code to construct the Fire Dragon like this:

class EnemyBehaviorList : EnemyBehavior {
    //take enemy behaviors in constructor
}

class FireBallState : EnemyBehaviorList {
    //to do something specific
}

Enemy fireDragon = new Enemy() {
    name = "FireDragon",
    behaviours = new List () {
        new FireBallState() {            //Fire Ball State
            new FireBallBehaviour(),     //Fire Ball Behavior
            new BehaviorA()
        },
        new BehaviorB()                 //Another State, can be a behavior
    },
    currentBehaviourIndex = 0
}


and the code to construct an enemy that will only generate fire ball:

Enemy fireDragon = new Enemy() {
    name = "FireDragon",
    behaviours = new List () {
        new FireBallBehaviour()
    },
    currentBehaviourIndex = 0
}

Software Design: Feature based design on class

This is about when to create a class: for a new feature instead of a new object concept
For example, an enemy for a combat game. Instead of making a class for an enemy, we should make a class for a feature/skill/behavior of enemy. Enemy should be a “data structure” instead of “controller”.

// Wrong practice:
class EnemyA : Enemy {
    ...
    public void throwFireBall() {...}  // it is hard to share this
                                       // with other enemy
}

// Correct practice:
class Enemy {
    string name; //name = "EnemyA"
    List<EnemyBehavior> behaviors;
}
class EBThrowFireBall : EnemyBehavior { //EB stands for EnemyBehavior
    public override void someEnemyMessage() {
        //throw fire ball
    }
}


Therefore different enemies can share same behavior with different settings and combination.

The key to make this decision is all about concept: enemy is a combination of behavior, and the behavior sharing between enemies are expected.