If you’ve used Flex, you’ve no doubt (er, hopefully) been using View States (AKA <mx:State>) to change the look of your RIA as it progresses through different situations of use. While this is immeasurably useful, it does not necessarily qualify as an implementation of the State Design Pattern which allows you to change not only how a component looks but how it functions as well.
(for more on design patterns, read my favorite book Head first design patterns).
Take the following example for a “Breakfast maker” application.
StateDemo.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:local="*" currentState="{ENGLISH}">
<mx:Script>
<![CDATA[
// define the state names as constants
static public const ENGLISH:String = "English";
static public const FRENCH:String = "French";
/**
* When the "make breakfast" button is clicked, display the steps
* you'll need to follow to make breakfast.
* Imagine that you'd need to implement all of these steps as functions
* and that there are several more languages to support.
*/
protected function onBreakfastButton(event:Event):void {
if (currentState == ENGLISH) {
output.text = "Fry eggs" +
"\nFry tomatoes" +
"\nToast bread" +
"\nMake tea";
}
if (currentState == FRENCH) {
output.text = "Tartiner du beurre sur baguette" +
"\nMettre le yaourt dans un bol" +
"\nTranche les fruits" +
"\nFaire cafe";
}
}
]]>
</mx:Script>
<!-- Define the changes to the view in the <mx:states> tags -->
<mx:states>
<mx:State name="{ENGLISH}">
<mx:SetProperty target="{breakfastButton}" name="label" value="Make Breakfast" />
</mx:State>
<mx:State name="{FRENCH}">
<mx:SetProperty target="{breakfastButton}" name="label" value=" Préparer des petits-déjeuners" />
</mx:State>
</mx:states>
<mx:VBox>
<mx:Button id="breakfastButton" click="onBreakfastButton(event)" />
<mx:TextArea id="output" height="100" width="250"/>
<mx:HBox>
<!-- Swap states using these buttons -->
<mx:Button label="English" click="{currentState = ENGLISH}" />
<mx:Button label="French" click="{currentState = FRENCH}" />
</mx:HBox>
</mx:VBox>
</mx:Application>
Compiling this code gives you the following…
View SWF
The problem
While there’s nothing inherently wrong with this application, there could be complications when trying to make changes to the logic. First of all, the onButtonClick() handler has to provide different functionality based on the currentState. The states themselves need to add labels to the breakfastButton. Imagine what would happen if you expanded this application to support 15 languages or to create lunch, dinner, and teatime. Things could get out of control fast!
The Solution – applying the state design pattern
The state design pattern allows you to separate out state specific functionality and defer the execution of methods to those state objects.

I’ll combine the state pattern with the view states by making a currentLogicalState property of the application. This setter will also automatically set the currentState based on a name stored in the currentLogicalState. This is easier done than said…
1. Define a logical state interface
My logical state interface only needs to have one getter for name so that we can associate it with a view state.
ILogicalState.as
package
{
public interface ILogicalState
{
function get name():String;
}
}
2. create a customized state interface for this application
This interface should contain any methods that are state dependent.
IBreakfastMakerState.as
package
{
public interface IBreakfastMakerState extends ILogicalState
{
function get breakfastInstructions():String;
function get breakfastLabel():String;
}
}
Note that this interface extends ILogicalState.
3. create state classes
I’ll create two different state classes, one for English breakfast and one for French Breakfast. Each will implement the IBreakfastMakerState interface and will provide the functionality needed to prepare the appropriate breakfast.
EnglishBreakfastState.as
package
{
public class EnglishBreakfastState implements IBreakfastMakerState
{
public function get name():String
{
return "English";
}
public function get breakfastInstructions():String
{
return "Fry eggs" +
"\nToast bread" +
"\nFry tomatoes" +
"\nMake tea";
}
public function get breakfastLabel():String {
return "Make Breakfast";
}
}
}
FrenchBreakfastState.as
package
{
public class FrenchBreakfastState implements IBreakfastMakerState
{
public function get name():String
{
return "French";
}
public function get breakfastInstructions():String
{
return "Tartiner du beurre sur baguette" +
"\nMettre le yaourt dans un bol" +
"\nTranche les fruits" +
"\nFaire cafe";
}
public function get breakfastLabel():String {
return "Préparer des petits-déjeuners";
}
}
}
4. Refactor StateDemo.mxml
Finally, rewrite the StateDemo.mxml to take advantage of the new state objects and call it LogicalStateDemo.mxml. (Note, this application should run exactly the same as StateDemo.mxml)
LogicalStateDemo.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:local="*"
initialize="onInitialize(event)">
<mx:Script>
<![CDATA[
// define the logical states as constants
static public const ENGLISH:IBreakfastMakerState = new EnglishBreakfastState();
static public const FRENCH:IBreakfastMakerState = new FrenchBreakfastState();
/**
* keep track of the current logical state and when the currentLogicalState changes
* update the currentState using its name property.
*/
protected var _currentLogicalState:IBreakfastMakerState;
public function set currentLogicalState(state:IBreakfastMakerState):void {
_currentLogicalState = state;
currentState = _currentLogicalState.name;
}
protected function onInitialize(event:Event):void {
currentLogicalState = ENGLISH;
}
protected function onBreakfastButton(event:Event):void {
// rely on the logical state to handle the logic of preparing the breakfast.
output.text = _currentLogicalState.breakfastInstructions;
}
]]>
</mx:Script>
<!-- Define the changes to the view in the <mx:states> tags -->
<mx:states>
<!-- Name each view state using the name of the logical state -->
<mx:State name="{ENGLISH.name}">
<!-- Change the label using a value from the logical state -->
<mx:SetProperty target="{breakfastButton}" name="label" value="{ENGLISH.breakfastLabel}" />
</mx:State>
<mx:State name="{FRENCH.name}">
<mx:SetProperty target="{breakfastButton}" name="label" value="{FRENCH.breakfastLabel}" />
</mx:State>
</mx:states>
<mx:VBox>
<mx:Button id="breakfastButton" click="onBreakfastButton(event)" />
<mx:TextArea id="output" height="100" width="250"/>
<mx:HBox>
<!-- Swap logical states using these buttons -->
<mx:Button label="English" click="{currentLogicalState = ENGLISH}" />
<mx:Button label="French" click="{currentLogicalState = FRENCH}" />
</mx:HBox>
</mx:VBox>
</mx:Application>
Et voila! A state pattern that works with the existing view states. This may seem like a lot of work for such a simple thing but it will really pay off in the long run on a complicated RIA.