State pattern and MVC pattern revisit: Take one

Use case: You need to change the behaviors of your type code at run-time. For example, clicks on “play” button sometimes play a video clip and in other cases do nothing, depending on the context. (whether the video clip is playing or not.) The bad smell in your code usually is signaled by the presence of case-like conditional statements. It gets too complex when you need to place in many of the methods the same logical case/if-else switches to tackle the context and execute different code. The solutions can be “Replace Conditional with Polymorphism“, “Replace Type Code with Subclasses” or “Replace Type Code with State/Strategy” (Martin Fowler: Refactoring).

In GUI design, the run-time states and behaviors of a set of user interfaces are determined by a set of predefined rules. These rules are usually procedural-like and can be visually presented in flow charts. Using a statechart, we can split the logics into a group of self-sustained substate objects and thus make our program much easier to control.

In order to make it work, you need to set up a polymorphic structure:

1. A generic state interface. It contains two methods: enter() and exit(). They are directly called by state context object (see #5) when a state decides to switch to another state. Implementing enter()/exit() in the concrete classes to perform type dependent setups and cleanup (such as hiding/fading in some components, etc.).

2. An application specific state interface. In this interface, define services that all the subclasses need to carry out, such as the following:

public function viewProfile(index:Number):Void
public function handleIndexSelect(info:Object):Void

3. An abstract/base state class that implements interfaces 1 and 2.

4. Most of times, instead of stuffing all the service methods in the base state class in step3, you will find it convenient to create a couple of concrete classes that provide common services more than one of the substates will perform and that are independent of the context, such as playVideo(index:uint):Void, pauseVideo():Void, setRepeatAll(v:Boolean):Void, handleClipComplete(evt:Object):Void, etc..

5. A state manager. State manager is the context object. It has the following major responsibilities:

a. Stores references, as well as defines accessors to all the substate objects.

b. Accessors to get and set the current state object. Like this:

public function setState(v:AbstractState):Void
{
if(_state!=v)
{
if(_state)_state.exit();
_state = v;
_state.enter();
}
}

c. Initializes the entry substate object (and optionally to initialize all the substates) and set the current state.

6. All the concrete substate classes. The classes need to store a reference to the state context object, and in the case of Flash, the main timeline (or the Document class if in ActionScript 3), as well as references to each of the common service provider classes as mentioned in step3 if any. Each of the classes has the following responsibilities:

a. Implements enter() and exit(). enter() usually needs to assign event handlers to all the UI controls and exit() usually removes these event handlers. You don’t switch states within exit() since state manager explicitly calls enter/exit within its setState() (and you will get “Error 1023 Stack overflow” at run-time if you do so.)

b. Defines context sensitive behaviors. They are usually handlers of UI events and can also be a condition such as when a rocket thrusts beyond a certain speed threshold. It’s up to these individual states to decide when to switch states and they do this by informing the state manager which state they choose to go to, like so:

private function gotoZoomOutState():void
{
_stateManager.setState(_stateManager.getZoomOutState());
}

c. At least one of the methods in step b. needs to call the state manager to switch the current substate.

I feel this is the point of State pattern in that it partitions all the complicated contextual logic into separate chunks who then individually take care of each condition (in a much more linear fashion since individual states only behave at their own levels and in the case when they need to affect others in the system, they communicate via the state context object). This approach makes coping (develop, maintain and extend) with complex system much easier.

Advertisements

Tags:

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: