• 18. “状态变化”模式 之State模式(状态模式)


    状态变化模式

    • 在组件构建过程中,某些对象的状态经常面临变化,如何对这些变化进行有效的管理?同时有维持高层模块的稳定?“状态变化”模式为这一问题提供了一种解决方案。
    • 典型模式
      • State
      • Memento

    1. 动机

    • 在软件构建过程中,某些对象的状态如果改变,其行为也会随之而发生改变,比如文档出于只读状态,其支持的行为和读写状态支持的行为就可能完全不同
    • 如何在运行时根据对象的状态来透明地更改对象的行为?而不会为对象操作和状态转化之间引入紧耦合?

    2. 代码示例

    比如我们有个网络应用,该应用会根据网络的状态进行一些行为上的调整,如下代码所示

    1. Network_Open,
    2. Network_Close,
    3. Network_Connect,
    4. };
    5. class NetworkProcessor{
    6. NetWorkState state;
    7. public:
    8. void Operation1(){
    9. if (state == Network_Open){ // 如果当前是打开状态,则经历过一系列操作之后,再恢复成关闭状态
    10. // ....
    11. state = Network_Close;
    12. }
    13. else if(state == Network_Close){ // 如果当前是关闭状态,经历过一些操作之后,设置成连接态
    14. // ...
    15. state = Network_Connect;
    16. }
    17. else if (state == Network_Connect){ // 如果当前是连接状态,经历过一些操作之后,设置成打开状态
    18. // ...
    19. state = Network_Open;
    20. }
    21. }
    22. void Operation2(){
    23. if (state == Network_Open){
    24. // ....
    25. state = Network_Connect;
    26. }
    27. else if(state == Network_Close){
    28. // ...
    29. state = Network_Open;
    30. }
    31. else if (state == Network_Connect){
    32. // ...
    33. state = Network_Close;
    34. }
    35. }
    36. void Operation3(){
    37. // ...
    38. }
    39. };

    结合上面代码和前面的动机对比理解:

    对象的状态如果改变,其行为也会随之而发生改变:Operation1中的if else就已经很清楚的表明了Operation1是根据你的状态不同,而行为不一样,但是在这个过程中它还会改变它的状态

    上面代码有什么问题,有前面学习印象的同学,会感觉这个问题似曾相识,这个很像是策略模式里面解决的问题,很多if else的bad Smell,上面出现的很多If else是有关业务状态,策略模式就已经告诉我们,对于这种情况,我们要问个为什么?

    上面代码中的枚举类型以后会不会有其他类型出现呢?如果添加了其他的状态,那我之前的代码应该怎么修改,必然是往里面继续添加else if,然后还要梳理新添加的状态和当前已经存在状态的关系,这明显违背了开闭原则

    3. 代码改进

    我们首先遵从策略模式里面的类似方式,试下是否可行,我们先提抽象基类

    1. // 这里其实就是将前面的枚举进行类型化的
    2. class NetworkState{
    3. public:
    4. NetworkState *pNext;
    5. virtual void Operation1() = 0;
    6. virtual void Operation2() = 0;
    7. virtual void Operation3() = 0;
    8. NetworkState(){}
    9. virtual ~NetworkState(){}
    10. };
    11. // 这里的代码其实就是将判断当前时候是open态的那部分逻辑抽离成了类,即Open态的类
    12. class OpenState : public NetworkState{
    13. static NetworkState *m_instance;
    14. public:
    15. static NetworkState* getInstance(){
    16. if (m_instance != nullptr){
    17. m_instance = new OpenState();
    18. }
    19. return m_instance;
    20. }
    21. void Operation1(){
    22. // ....这段省略逻辑其实就是前面
    23. // void Operation1(){
    24. // if (state == Network_Open){
    25. // 中省略的逻辑的逻辑
    26. pNext = CloseState::getInstance();
    27. }
    28. void Operation2(){
    29. // ...
    30. pNext = ConnectState::getInstance();
    31. }
    32. void Operation3(){
    33. // ...
    34. }
    35. };
    36. // 下面两个类和前面这个类似
    37. class CloseState : public NetworkState{
    38. };
    39. class ConnectState : public NetworkState{
    40. };
    41. // 网络应用这一层,塞的就不是一个枚举了,而是一个真正的状态对象
    42. class NetworkProcessor{
    43. NetworkState *pState; // 1. 其实这里,和前面的结果是一样的,只是调用的方法变了而已
    44. public:
    45. NetworkProcessor(NetworkState *pState){
    46. this->pState = pState;
    47. }
    48. void Operation1(){
    49. // ..
    50. pState->Operation1(); // 2. 再看这句话,其实虚函数的本质就是运行时的if else即,如果pState这个指针指向的是open态,那么就会调用Open态的Operation1
    51. pState = pState->pNext; // 3. 执行完后,就将它的状态修改为它的下一个状态,这个下一个状态是当前状态对象里面决定的
    52. // ...
    53. }
    54. void Operation2(){
    55. // ..
    56. pState->Operation2();
    57. pState = pState->pNext;
    58. // ...
    59. }
    60. void Operation3(){
    61. // ..
    62. pState->Operation3();
    63. pState = pState->pNext;
    64. // ...
    65. }

    这样做了之后好处是什么,其实和策略模式好处异曲同工,当我们状态增加的时候,其实我们只要增加一个类就可以了,比如增加了一个wait状态,只需要增加一个类似下面的类,并且要在这个类里面维护自己的下一个状态,和OpenState一样,像NetworkProcessor这个类完全不需要修改

    1. class WaitState : public NetworkState{
    2. };

    4. 模式定义

    允许一个对象在其内部状态改变的时候改变它的行为(其实里里面使用的是多态,即前面代码中NetworkProcessor中维护的NetworkState *pState;)。从而使对象看起来似乎修改了其行为。

                                                                                                                 ------《设计模式》GOF

    5. 结构

     

     

     State里面一般是多个行为而不是一个Handle(),当然也可以是一个行为,看起来一个行为的时候和策略模式没有什么两样

    6. 要点总结

    • State模式将所有与一个特定状态相关的行为(Operation1、2、3)都放入到一个State的子类对象中,在对象状态切换时,切换相应的对象;但同时维持State的接口,这样实现了具体操作于状态转换之间的解耦。
    • 为不同的状态引入不同的对象使得状态转换变得更加明确,而且可以保证不会出现状态不一致的情况,因为转换是原子性的---即要么彻底装换过来,要么不转换

            (第一版代码各个操作中夹杂着各种状态的切换,很乱很杂,但是我们最后的版本,一个状态只需要关系各个操作之后的下一个状态是什么就好了,比如:operation1之后的状态是什么,2之后的状态是什么,3之后的状态是什么,就很清晰)

    • 如果State对象没有实例变量(即没有后继态之后的实例),那么上下文可以共享一个State对象(代码中的单例),从而节省对象开销。
  • 相关阅读:
    大小仅为人血细胞的1/10?揭秘纳米机器人的厉害之处
    1.http和https
    数据结构与算法之美学习笔记:17 | 跳表:为什么Redis一定要用跳表来实现有序集合?
    STM32的C语言重点知识(1.C语言数据类型+2.C语言宏定义+3.C语言typedef+4.C语言结构体+5.C语言枚举)
    Flutter 环境配置
    python3-GUI概述及应用
    第四章-项目整合管理
    python之requests的高级用法
    RecyclerView的好朋友 — SnapHelpter
    [工业自动化-22]:西门子S7-15xxx编程 - 软件编程 - 如何PLC建立用户界面: SIMATIC 面板式HMI 或工控机PC HMI
  • 原文地址:https://blog.csdn.net/bocai1215/article/details/127660182