Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Cell Phone - An implementation of behavioral pattern to describe the states

0.00/5 (No votes)
15 Apr 2005 1  
An article on the State pattern describing cell phone states behavior.

Sample Image

Introduction

A Finite State Machine (FSM) is the significant model for designing dynamic behavior, i.e., to receive events from a finite collection and keep track of the current state and the previous state, and provide a mechanism for changing states based on an event from a set of predetermined events. The implementation is the state-transition system based on the trendy cell phone activities developed in .NET.

Finite State Machine

No FSM is an isolated process. Every aspect of an FSM depends on its context, requirements, limitation of programming language and other factors. The most popular non-object oriented implementations of FSM are cascading if statements, nested switch statements, state transition tables and code using goto statements. Their popularity is due to fast execution, but at the cost of weaker reliability and maintainability. On the other hand, the pure Object-Oriented FSM designs require �Complete Reification�, that is, making every element of the state machine an object. Finite State Machine implementation comes from �the Gang of Four� (GoF) with their design pattern �The State Pattern�. This is an object-oriented approach with a tightly coupled structure of classes or class hierarchy for each state and event.

A Finite State Machine has at least two common characteristics:

  1. First, a distinct, finite number of states exist.
  2. Second, methods exist that facilitate movement between the states, i.e., transition.

The State Pattern

Allow an object to alter its behavior when its internal state changes. The object will appear to change its class. The solution is provided in the following way; each state is encapsulated in a separate class that implements the common State interface (IState) and defining the behavior to handle all external events. The Context class is responsible for maintaining the knowledge of the current state and other data. External entities communicate with the system via the Context class that forwards requests to the current state object. There are the following consequences while using the State Pattern.

  1. State specific behavior is localized and partitioned between different states.
  2. State transition is performed exactly in the code.
  3. If an object has many states, many concrete classes need to be created, which may result in an excessively large class hierarchy.
  4. The Context class delegates all external events to corresponding methods in the interface of the State class; it is responsible for providing the correct mapping. This results in a tight coupling between the Context class and concrete events.
  5. The Context class keeps one instance of each state in a hash table so that state changes does not result in creation or deletion of objects.
  6. The Context class keeps many state objects, but only one is active (i.e., responds to events) at a given time.

States

Sample Image

  • Idle State � system is by default in Idle state. Moreover, whenever we cancel the other states, system switches to �Idle� sate.
  • Menu State � system is in Idle state. If we invoke Menu command, state changes to Menu with the operation �Menu List�, and if we invoke Call command, state changes to Menu with the operation �LastCall List�.
  • Dialing State � system is in Menu state. If we invoke Call command, state changes to Dialing with the operation �Calling� and this Calling state is then triggered to �Talking� with operation �Call Connected� after some wait, provided in the meanwhile we did not �Cancel� the state.
  • Ringing State � system is in Idle state. If we invoke Ringing command, state changes to Ringing with the operation �Call is Coming�. If we invoke Call command, state changes to Talking with the operation �Answering�, and if we invoke Cancel command, state changes to Idle after disconnecting the action.

Special Cases

  • If we are in Idle state and we invoke the Cancel command, system gives message �Already in Idle state�.
  • If we are in Menu state and we invoke the Menu command, system gives message �Already in Menu state�.
  • If we are in Dialing/Talking state and we invoke the Call command, system gives message �Already in Dialing/Talking state�.
  • If we are in Talking state and we invoke the Menu command, system gives message �Operation Ignored: Currently in Talking State�.
  • If we are in Ringing state and we invoke the Menu command, system gives message �Operation Ignored: Currently in Ringing State�.

Class Diagram

Sample Image

IState interface

The IState interface is how you implement the actual "intelligence". The idea is that an entity can only be in one state at any given time, so you can use this interface to dictate what happens when that state is entered, when to leave that state, and where to go.

interface IState
{
    void menuButton(CellPhone cellPhone);
    void callButton(CellPhone cellPhone);
    void exitButton(CellPhone cellPhone);
}

CellPhone Class

This simply dictates that the entity must keep track of the current state, the previous state, and provide a mechanism for changing state. I added the property m_previousState because it might make the state machine a bit more versatile if your code is able to know what state it was previously in, thus having the ability to change its behavior.

public class CellPhone
{
    private Form1 m_form; 
    private IState m_previousState;
    private IState m_currentState;
    private Hashtable m_states = new Hashtable();

    public CellPhone(Form1 f)
    {
        m_states.Add( eState.IDLE, new Idle());
        m_states.Add( eState.MENU, new Menu());
        m_states.Add( eState.RINGING, new Ringing());
        m_states.Add( eState.DIALING, new Dialing());
        m_states.Add( eState.TALKING, new Talking());
    
        //default stste is idle

        m_currentState = (IState) m_states[eState.IDLE]; 
        m_previousState = null; 
        m_form = f;
        disconnect();
    }
    public eState getCurrentState()
    {
        foreach(object key in m_states.Keys)
        {
            if((IState)m_states[key] == m_currentState)
                return (eState)key;
        }    
        return eState.UNKNOWN;
    }
    public void setCurrentState(eState s)
    {
        m_previousState = m_currentState;
        m_currentState = (IState) m_states[s];    
    }
    
    //Wrappers to m_currentState

    public void menuButton()
    {
        m_currentState.menuButton( this );
    }
    public void callButton()
    {
        m_currentState.callButton( this );
    }
    public void exitButton()
    {
        m_currentState.exitButton( this );
    }
    public void ringButton()
    {
        setCurrentState( eState.RINGING );
        ringing();
    }

    //More functions

    public void call() {    Show("Calling...");    } 
    public void showLastCall() {    Show("LastCall List...");    } 
    public void showMenu() {    Show("Menu List...");    } 
    public void talking() {    Show("Call Connected...");    } 
    public void ringing() {    Show("Call is coming...");    } 
    public void answer() {    Show("Answering...");    } 
    public void disconnect() {    Show("Disconnected/Idle...");    } 

    public void Show(string s)
    {
      ListViewItem item = new ListViewItem();
      item.Text = s;
      if(m_previousState != null)
       item.SubItems.Add( m_previousState.ToString().Replace("StatePattern.", ""));
      else
       item.SubItems.Add( "Unknown" );
      item.SubItems.Add(m_currentState.ToString().Replace("StatePattern.", ""));
      m_form.MainListView.Items.Add( item );

      m_form.LState.Text = 
       m_currentState.ToString().Replace("StatePattern.", "");
      m_form.LOperation.Text = s;
    }
    public void ignore()
    {
        string str = String.Format(@"Ignored: Currently in {0} state", 
                                           m_currentState.ToString());
        m_form.MainListView.Items.Add( str.Replace("StatePattern.", "") );
    }
    public void repeat()
    {
        string str = String.Format(@"Already in {0} state", 
                                m_currentState.ToString());
        m_form.MainListView.Items.Add( str.Replace("StatePattern.", "") );
    }
}

Concrete Classes

The Concrete classes are going to be the concrete implementation of the IState interface. Notice how each class implements the IState interface to define the rules when states transition to another state. Alpha class is a thread to automatically trigger the state from calling to talking state.

class Idle : IState
{
    public void menuButton(CellPhone cellPhone)
    {
        cellPhone.setCurrentState( eState.MENU );
        cellPhone.showMenu();
    }
    public void callButton(CellPhone cellPhone)
    {
        cellPhone.setCurrentState( eState.MENU );
        cellPhone.showLastCall();
    }
    public void exitButton(CellPhone cellPhone)
    { 
        cellPhone.repeat();
    }
}

class Menu : IState
{
    public void menuButton(CellPhone cellPhone)
    {
        cellPhone.repeat();
    }
    public void callButton(CellPhone cellPhone)
    {
        Alpha a = new Alpha( cellPhone );
        Thread t = new Thread(new ThreadStart(a.threadProc));
        t.Start();
            
    }
    public void exitButton(CellPhone cellPhone)
    { 
        cellPhone.setCurrentState( eState.IDLE );
        cellPhone.disconnect();
    }
}

class Ringing : IState
{
    public void menuButton(CellPhone cellPhone)
    { 
        cellPhone.ignore();
    }
    public void callButton(CellPhone cellPhone)
    {
        cellPhone.setCurrentState( eState.TALKING );
        cellPhone.answer();
    }
    public void exitButton(CellPhone cellPhone)
    { 
        cellPhone.setCurrentState( eState.IDLE );
        cellPhone.disconnect();
    }
}

class Dialing : IState
{
    public void menuButton(CellPhone cellPhone)
    { 
        cellPhone.ignore();
    }
    public void callButton(CellPhone cellPhone)
    { 
        cellPhone.repeat();
    }
    public void exitButton(CellPhone cellPhone)
    {
        cellPhone.setCurrentState( eState.IDLE );
        cellPhone.disconnect();
    }
}

class Talking : IState
{
    public void menuButton(CellPhone cellPhone)
    { 
        cellPhone.ignore();
    }
    public void callButton(CellPhone cellPhone)
    {
        cellPhone.repeat();
    }
    public void exitButton(CellPhone cellPhone)
    { 
        cellPhone.setCurrentState( eState.IDLE );
        cellPhone.disconnect();
    }
}

Thanks

My deep thanks to Agha M. Ali for his suggestions on the article.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here