Links

Categories

Tags


« | Main | »

Implementing state-variant classes

By Jewe | May 23, 2013

This example is a part of my series on implementing state machines.

Imagine we want to implement a class that acts like a state machine. That is, the methods of the class should change their behavior, depending on the state the class is currently in.

The class could look something like this:

class Actor {
    method A();
    method B();
    method C();
}

The methods A, B and C are the actual behaviors of our state object. They represent different things that the actor can do within the same state. To be able to set the object to a specific state, we need more methods:

class Actor {
    method A();
    method B();
    method C();
    method S1();
    method S2();
    method S3();
}

The methods S1, S2 and S3 represent possible states that our actor can enter. When calling one of the S methods, the behaviors A, B and C will change to what is defined for that state.

Using this state-variant class should look like this:

// create an Actor
Actor actor = new Actor();
// call its behaviors
actor.A();
actor.B();
actor.C();
// change state
actor.S2();
// call again - same methods, different behavior
actor.A();
actor.B();
actor.C();

This problem can be very easily implemented using delegates for the methods A, B and C. We could declare some delegates in our Actor class, and then add the necessary variables. However, in real life such a class would probably expose more than just three behaviors, so adding all the delegates manually could be quite a lot of work.

Fortunately, JewelScript can do this work automatically for us if we use hybrid inheritance. For this, we just need to define a base class containing all the methods we want to use as behaviors. This class will be the “blueprint” that the compiler uses to create delegates in our Actor class:

class StateObject {
    method StateObject() {}
    method A() {}
    method B() {}
    method C() {}
}

Our next step is to define our Actor class and have it “hybridize” our StateObject:

class Actor hybrid StateObject {

    method Actor() hybrid (new StateObject()) {
        S1();
    }
    method S1() {
        A = method { }; // implement Actor.S1.A
        B = method { }; // implement Actor.S1.B
        C = method { }; // implement Actor.S1.C
    }
    method S2() {
        A = method { };
        B = method { };
        C = method { };
    }
    method S3() {
        A = method { };
        B = method { };
        C = method { };
    }
}

I added no code to any of the methods. Needless to say, we don’t have to use names like “A, B, C” for the behaviors, and of course a real life class would also define parameters for these methods. I’m just leaving out these details to keep the example simple and understandable.

Anyway, it should be clear to see what happens when we call one of the state setters S1, S2 and S3. They actually override the delegates we have “inherited” from StateObject with a reference to an anonymous local method. This not only saves us some typing effort, but it also hides the actual implementation from the class and the outside world. The code is unreachable, unless it is referenced by one of the behaviors.

Having all delegates in one class also has the added perk that they all share the same object and have access to it’s members.

As you can see, I have chosen to initialize the object with S1 in the constructor. However, that is open to choice. We could also remove the call. In that case A, B and C would simply do nothing, or whatever is implemented in StateObject, until one of the state setters is called.

Lastly, what would we do if we had one behavior that shouldn’t change state? For example, there could be a case where behavior C should not change between S2 and S3. This is how we can achieve that:

class Actor hybrid StateObject {

    method Actor() hybrid (new StateObject()) {
        S1();
    }
    method S1() {
        A = method { };
        B = method { };
        C = method { };
    }
    method S2() {
        A = method { };
        B = method { };
        C = S2_S3_C;
    }
    method S3() {
        A = method { };
        B = method { };
        C = S2_S3_C;
    }
    method S2_S3_C() { }    // shared between states
}

Topics: coding tips, docs | Comments Off on Implementing state-variant classes

Comments are closed.