65.9K
CodeProject 正在变化。 阅读更多。
Home

手机 - 行为模式的实现, 用于描述状态

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.75/5 (10投票s)

2005年4月15日

4分钟阅读

viewsIcon

58579

downloadIcon

224

一篇关于状态模式的文章,描述了手机的状态行为。

Sample Image

引言

有限状态机 (FSM) 是设计动态行为的重要模型,即接收有限集合中的事件,并跟踪当前状态和前一状态,同时提供一种基于预定事件集合中事件改变状态的机制。该实现是基于时下流行的手机活动,使用 .NET 开发的状态转换系统。

有限状态机

没有 FSM 是一个孤立的过程。FSM 的每个方面都取决于其上下文、需求、编程语言的限制和其他因素。最流行的非面向对象 FSM 实现是链式的 if 语句、嵌套的 switch 语句、状态转换表和使用 goto 语句的代码。它们的流行是因为执行速度快,但可靠性和可维护性较弱。另一方面,纯粹的面向对象 FSM 设计需要“完全具体化”,即使状态机的每个元素都成为一个对象。有限状态机的实现来自“Gang of Four”(GoF) 的设计模式“状态模式”。这是一种面向对象的方法,每个状态和事件都对应一个紧耦合的类结构或类层次结构。

有限状态机至少有两个共同特征

  1. 第一,存在明确的、有限数量的状态。
  2. 第二,存在促进状态之间移动的方法,即转换。

状态模式

允许对象在内部状态改变时改变其行为。对象看起来会改变其类。解决方案如下:每个状态都封装在一个实现通用 State 接口 (IState) 的单独类中,并定义处理所有外部事件的行为。Context 类负责维护当前状态和其他数据的知识。外部实体通过 Context 类与系统通信,该类将请求转发给当前状态对象。使用状态模式时有以下后果。

  1. 特定于状态的行为被局部化并划分到不同的状态之间。
  2. 状态转换完全在代码中执行。
  3. 如果一个对象有很多状态,就需要创建很多具体类,这可能导致类层次结构过于庞大。
  4. Context 类将所有外部事件委托给 State 类接口中的相应方法;它负责提供正确的映射。这导致 Context 类与具体事件之间紧密耦合。
  5. Context 类在哈希表中保留每个状态的一个实例,这样状态更改就不会导致对象的创建或删除。
  6. Context 类保留许多状态对象,但一次只有一个是活动的(即响应事件)。

状态

Sample Image

  • 空闲状态 - 系统默认处于空闲状态。此外,无论何时取消其他状态,系统都会切换到“空闲”状态。
  • 菜单状态 - 系统处于空闲状态。如果我们调用菜单命令,状态将变为菜单状态,操作为“菜单列表”;如果我们调用呼叫命令,状态将变为菜单状态,操作为“上次呼叫列表”。
  • 拨号状态 - 系统处于菜单状态。如果我们调用呼叫命令,状态将变为拨号状态,操作为“正在呼叫”,并且在等待一段时间后,如果在此期间我们没有“取消”状态,该呼叫状态将变为“通话中”,操作为“呼叫已连接”。
  • 响铃状态 - 系统处于空闲状态。如果我们调用响铃命令,状态将变为响铃状态,操作为“来电”。如果我们调用呼叫命令,状态将变为通话状态,操作为“接听”;如果我们调用取消命令,则在断开连接后状态将变为空闲。

特殊情况

  • 如果我们处于空闲状态并调用取消命令,系统会给出消息“已处于空闲状态”。
  • 如果我们处于菜单状态并调用菜单命令,系统会给出消息“已处于菜单状态”。
  • 如果我们处于拨号/通话状态并调用呼叫命令,系统会给出消息“已处于拨号/通话状态”。
  • 如果我们处于通话状态并调用菜单命令,系统会给出消息“操作被忽略:目前处于通话状态”。
  • 如果我们处于响铃状态并调用菜单命令,系统会给出消息“操作被忽略:目前处于响铃状态”。

类图

Sample Image

IState 接口

IState 接口是实现实际“智能”的方式。其思想是,一个实体在任何给定时间只能处于一种状态,因此您可以使用此接口来规定进入该状态时会发生什么,何时离开该状态,以及去往何处。

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

CellPhone 类

这很简单地规定了实体必须跟踪当前状态、前一状态,并提供改变状态的机制。我添加了 m_previousState 属性,因为如果代码能够知道它之前处于什么状态,从而能够改变其行为,那么状态机可能会更具通用性。

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.", "") );
    }
}

具体类

具体类将是 IState 接口的具体实现。请注意,每个类如何实现 IState 接口来定义状态转换到另一个状态时的规则。Alpha 类是一个线程,用于自动将状态从呼叫状态触发到通话状态。

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();
    }
}

谢谢

我非常感谢 Agha M. Ali 对本文提出的建议。

© . All rights reserved.