15设计模式_状态模式_C语言实现

状态模式

状态模式顾名思义就是实现状态机时使用的一种模式。

在嵌入式软件开发中,我们经常会遇到各种状态机。举个最简单的例子:一个设备有两种状态,如下图:

在这里插入图片描述

通常,我们实现该状态机的伪码如下:

enum StateEnum {
    OpenState;
    CloseState;
};

enum OperatinEnum {
    LongPressButton; 
    ShortPressButton;    
}

int StateProcess(int currentState, int operation) 
{
    if (operation == LongPressButton) {
        if (currentState == OpenState) {
            // ...
            return CloseState;
        }
    } else if (operation == ShortPressButton) 
        if (currentState == CloseState) {
            // ...
            return OpenState;
        }
    }

    // 状态切换失败,保持当前状态
    return currentState; 
}

如果我们要增加sleep状态,如下图:

在这里插入图片描述

那么,我们需要把代码修改为如下(带"//–add"的行是本次修改的代码):

enum StateEnum {
    OpenState;
    CloseState;
    SleepState; //--add
};

enum OperatinEnum {
    LongPressButton; 
    ShortPressButton; 
    FreeOver5Mins; //--add
}

int StateProcess(int currentState, int operation) 
{
    if (operation == LongPressButton) {
        if (currentState == OpenState) {
            // ...
            return CloseState;
        }
    } else if (operation == ShortPressButton) 
        if (currentState == CloseState) {
            // ...
            return OpenState;
        } else if (currentState == SleepState) { //--add
            // ...                               //--add
            return OpenState;                    //--add
        }
    } else if (operation == FreeOver5Mins) {     //--add
        if (currentState == OpenState) {         //--add
            // ...                               //--add
            return SleepState;                   //--add
        }
    }

    // 状态切换失败,保持当前状态
    return currentState; 
}

这还只是一个非常非常简单的状态机。但说实话,写到这个程度我已经很难检查代码的实现是否正确了。所以,这种实现方式的缺点显而易见:1) if-else太多太复杂,即使比较简单的状态机也很难让人脑一下子看明白;2) 明显违反了开闭原则。

那么,按照GOF的状态模式的思想,我们应该怎么来实现呢?我们应该按“状态”将上面的代码拆分开,把每种状态作为一个单独的类。当增加sleep状态时,我们就增加sleep状态类。伪码如下:

enum StateEnum {
    OpenState;
    CloseState;
    SleepState;        //--add
};

enum OperatinEnum {
    LongPressButton; 
    ShortPressButton; 
    FreeOver5Mins;     //--add
}

// 定义State基类
struct State {
    struct State* (*LongPressButtonOperation)(); // LongPressButton的操作,返回next state
    struct State* (*ShortPressButtonOperation)();
    struct State* (*FreeOver5MinsOperation)();   //--add
}

// 定义OpenState类,继承State基类
struct OpenState {
    ... 根据本状态迁移的实际情况,重写基类中的方法
}

// 定义CloseState类,继承State基类
struct CloseState {
    ... 根据本状态迁移的实际情况,重写基类中的方法
}

// 定义SleepState类,继承State基类                   //--add
struct SleepState {                                  //--add
    ... 根据本状态迁移的实际情况,重写基类中的方法    //--add
}

// 定义StateProcessor(),驱动状态迁移
StateProcessor() {
    ...
}

这里我只简单的写了下状态模式的伪码,不打算详细展开了。主要原因是,我个人觉得,这种实现方式虽然明显比最前面的方式有所改进。但我还是觉得这样实现状态机并不是很好。因为这种方式主要解决了增加状态的问题,并没有解决增加操作的问题,然后再实际中,一旦状态机发生变化,大多数场景都是状态和操作都有变化。就前面的例子来说,增加sleep状态,同时就增加了FreeOver5Mins操作,且open状态也会增加FreeOver5Mins操作。

反正,我是没怎么见过状态机增加状态时,不增加操作的情况。所以,我自己通常会按另一个方式来实现状态机,我会把每种 current state – operation – next state 作为一个类,然后用StateProcessor驱动状态迁移。

扫描二维码关注公众号,回复: 11674228 查看本文章
enum StateEnum {
    OpenState;
    CloseState;
    SleepState;        //--add
};

enum OperatinEnum {
    LongPressButton; 
    ShortPressButton; 
    FreeOver5Mins;     //--add
}

// 定义State--Operation基类
struct State {
    int currentState;
    int operation;
    int (*Operation)(); 
    int nextState;
}

struct State g_fsm[] = {
    { OpenState,
      LongPressButton,
      xxxOperation(), // 根据本状态迁移的实际情况,重写基类中的方法
      CloseState
    },
    { CloseState,
      ShortPressButton,
      xxxOperation(), // 根据本状态迁移的实际情况,重写基类中的方法
      OpenState,
    },
    { OpenState,       //--add
      FreeOver5Mins,   //--add
      xxxOperation(),  //--add
      SleepState,      //--add
    },
    { SleepState,       //--add
      ShortPressButton, //--add
      xxxOperation(),   //--add
      OpenState,        //--add
    },
};

// 定义StateProcessor(),驱动状态迁移
int StateProcessor(int currentState, int operation) {
    for (i = 0; i < g_fsm[]的成员数; i++) {
        if (currentState == g_fsm[i].currentState) && (operation == g_fsm[i].operation) {
            g_fsm[i].xxxOperation(); 
            return g_fsm[i].nextState;
        }
    }

    // 状态切换失败,保持当前状态
    return currentState; 
}

我认为这种方式跟GOF的状态模式是有区别的,但我个人认为这种方式更完美。

猜你喜欢

转载自blog.csdn.net/weixin_46826913/article/details/108190516