Android, Flash, Flex, and PHP

Portfolio Site and Blog

Android, Flash, Flex, and PHP header image 2

Android Architecture: Part 8, State Pattern

November 27th, 2011 · 3 Comments · Android, OOP

This is the part where I address what’s happening in TapController that I’ve been avoiding until now. Unlike MVC and DOAs, I wouldn’t consider the State Pattern to be an essential part of Android architecture. However, it certainly is rad and it can do some cool things. Between that and because I’ve used it in TapController when dealing with messages its worth taking a moment to discuss.

The State Pattern:
So far in this series I haven’t addressed too much of what things are just how to do them in Android. However in this part, we’ll dive a little deeper into the what. The State Pattern is an Object Oriented Pattern where its purpose is to allow dynamic switching of behavior. Remember, objects have states (properties) and behaviors (methods). If you’re thinking that sounds a lot like the Strategy Pattern, yup, you’re right. The difference here is the intent of the pattern. The State Pattern is used to change behaviors based upon an object’s state. The calling client doesn’t even know the swap in objects have taken place. So how do you go about allowing dynamic switching of behaviors? We need to encapsulate those behaviors in objects and have them share a common interface. Let’s talk specifics as it pertains to the project “Tap Counter” we’ve been looking at.

The two objects or states that we’ll be looking at are UnlockedState and LockedState.  When CounterVo is set to locked, it means we don’t want any of it’s properties to change. For instance, if a user were to push the increment button the counter does not increment in this state. But if CounterVo is not locked, then the pushing the increment button would change the count. These are two different behaviors based upon the state of CounterVo.locked.

When TapController.handleMessage(…) is called, it delegates all responsibility to the one of these states. Which one is determined by whichever is referenced in TapController.messageState. Let’s see some TapController delegation.

@Override
public boolean handleMessage(int what) {
    return messageState.handleMessage(what);
}

Wow. That’s super simple and easy. So how does the reference in messageState change?

The states know about each other. When MESSAGE_UPDATE_LOCK is handled by UnLockedState it updates messageState to reference LockedState. And when MESSAGE_UPDATE_LOCK is handled by LockedState it updates messageState to reference UnLockedState. I think that will make more sense if we see it in code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.musselwhizzle.tapcounter.controllers;
 
public class UnlockedState extends TapState {
 
    // ... properties
 
    public UnlockedState(TapController controller) {
        super(controller);
    }
 
    @Override
    public boolean handleMessage(int what) {
        switch(what) {
            case TapController.MESSAGE_INCREMENT_COUNT:
                moveCount(1);
                return true;
            case TapController.MESSAGE_DECREMENT_COUNT:
                moveCount(-1);
                return true;
            case TapController.MESSAGE_RESET_COUNT:
                model.setCount(0);
                return true;
            default:
                return super.handleMessage(what);
        }
 
    }
 
    @Override
    public boolean handleMessage(int what, Object data) {
        switch(what) {
            case TapController.MESSAGE_UPDATE_LOCK:
                updateLock((Boolean)data);
                return true;
            case TapController.MESSAGE_UPDATE_LABEL:
                updateLabel((String)data);
                return true;
            case TapController.MESSAGE_KEY_EVENT:
                return handleKeyEvent((KeyEvent)data);
            default:
                return super.handleMessage(what, data);
        }
    }
 
    private boolean handleKeyEvent(KeyEvent event) {
        // .. handles the key event
    }
 
    private void moveCount(int amount) {
        model.setCount(model.getCount()+amount);
    }
 
    private void updateLock(boolean lock) {
        model.setLocked(lock);
        controller.setMessageState(new LockedState(controller));
    }
 
    private void updateLabel(String label) {
        model.setLabel(label);
    }
}

To talk specifics, UnlockedState tells the controller to change the state to LockedState as shown here.

controller.setMessageState(new LockedState(controller));

Now that we can change states and understand the delegation happens transparently, let’s compare how UnlockedState and UnlockedState handle the same message of TapController.MESSAGE_INCREMENT_COUNT. In UnlockedState, the count increments just as you might expect like so

@Override
public boolean handleMessage(int what) {
    switch(what) {
        case TapController.MESSAGE_INCREMENT_COUNT:
	model.setCount(model.getCount()+1);
	return true;
    }
}

And now check out LockedState.

@Override
public boolean handleMessage(int what) {
    switch(what) {
        case TapController.MESSAGE_INCREMENT_COUNT:
	return true;
    }
}

It does nothing but returns true to mean the message was handled.

How was the message handled? It didn’t do anything!

Exactly. In LockedState the appropriate response is to do nothing when asked to handle MESSAGE_INCREMENT_COUNT. When an object is used to intelligently do nothing, this is often referred to as a Null Object Pattern.

So why did you go through all the work to do this? Couldn’t you just have wrote an if statement to check the CounterVo.locked property and then do the appropriate actions in an if-else clause?

We could have done that, but then your if-else is being used to manage state. As states get more complex and there are more states your if-else will get longer and longer. Your code will be harder maintain, and if you have to make future changes you will have to open up old code and refactor. With states you can easily add all new behaviors in a new state and you’ll be following the Open/Closed Principle. However, in our exact case, yea…, it might have been overkill. But keep in mind the project “Tap Counter” is a discussion piece. I tried to create a simple but real project that used real solutions and architecture.

 

Example Files:
If you haven’t done so yet, grab the sources of our example here.

Tags: ··

3 responses so far ↓

  • 1 Android Architecture: Part 8, State Pattern // Nov 28, 2011 at 10:03 pm

    [...] See original here: Android Architecture: Part 8, State Pattern [...]

  • 2 carbodrache // Jan 3, 2012 at 2:39 am

    Hello,

    very good tutorial about MVC, but I have a comment about the State Pattern: for Android, running on a phone, it is preferable to limit the creation of unnecessary items to avoid the use of garbage collector. But in your code, for each state change a new one (locked or unlocked) is created, would not it be better to enforce the Singleton Pattern for these classes?

  • 3 musselwhizzle // Jan 3, 2012 at 2:50 am

    @carbodrache, Reusing instances is definitely better on performance. And rather than using a Singleton we could have added two methods to TapController getLockedState() and getUnlockedState() to return recycled instances. However, I chose not to as performance concerns are not an issue. Had this been a game or something CPU/Memory taxing I would have done that, but creating new objects every once in a while is not a big deal for something like this. Your question is similar to the concept of “object pooling” should you want to research it more. Thanks for reading. Cheers!

Leave a Comment


4 × nine =