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

状态变化模式

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

1. 动机

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

2. 代码示例

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

    Network_Open,
    Network_Close,
    Network_Connect,
};

class NetworkProcessor{
    NetWorkState state;

public:
    void Operation1(){
        if (state == Network_Open){ // 如果当前是打开状态,则经历过一系列操作之后,再恢复成关闭状态

            // ....
            state = Network_Close;
        }        
        else if(state == Network_Close){ // 如果当前是关闭状态,经历过一些操作之后,设置成连接态
            // ...
            state = Network_Connect;  
        }
        else if (state == Network_Connect){ // 如果当前是连接状态,经历过一些操作之后,设置成打开状态
            // ...
            state = Network_Open;  
        }
    }

    void Operation2(){
        if (state == Network_Open){

            // ....
            state = Network_Connect;
        }        
        else if(state == Network_Close){ 
            // ...
            state = Network_Open;  
        }
        else if (state == Network_Connect){
            // ...
            state = Network_Close;  
        }
    }

    void Operation3(){
        // ...
    }
};

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

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

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

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

3. 代码改进

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

// 这里其实就是将前面的枚举进行类型化的
class NetworkState{
public:
    NetworkState *pNext;
    virtual void Operation1() = 0;
    virtual void Operation2() = 0;
    virtual void Operation3() = 0;

    NetworkState(){}
    virtual ~NetworkState(){}
};


// 这里的代码其实就是将判断当前时候是open态的那部分逻辑抽离成了类,即Open态的类
class OpenState : public NetworkState{
    static NetworkState *m_instance;
public:
    static NetworkState* getInstance(){
        if (m_instance != nullptr){
            m_instance = new OpenState();
        }
        return m_instance;
    }

    void Operation1(){

        // ....这段省略逻辑其实就是前面
        // void Operation1(){
        // if (state == Network_Open){
        // 中省略的逻辑的逻辑 

        pNext = CloseState::getInstance();
    }

    void Operation2(){
        // ...
        pNext = ConnectState::getInstance();
    }

    void Operation3(){
        // ...
    }
};

// 下面两个类和前面这个类似
class CloseState : public NetworkState{

};

class ConnectState : public NetworkState{

};

// 网络应用这一层,塞的就不是一个枚举了,而是一个真正的状态对象
class NetworkProcessor{
    NetworkState *pState; //  1. 其实这里,和前面的结果是一样的,只是调用的方法变了而已
public:
    NetworkProcessor(NetworkState *pState){
        this->pState = pState;
    }

    void Operation1(){
        // ..
        pState->Operation1();  //  2. 再看这句话,其实虚函数的本质就是运行时的if else即,如果pState这个指针指向的是open态,那么就会调用Open态的Operation1
        pState = pState->pNext; // 3. 执行完后,就将它的状态修改为它的下一个状态,这个下一个状态是当前状态对象里面决定的
        // ...
    }

    void Operation2(){
        // ..
        pState->Operation2();
        pState = pState->pNext;
        // ...
    }

    void Operation3(){
        // ..
        pState->Operation3();
        pState = pState->pNext;
        // ...
    }

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

class WaitState : public NetworkState{
};

4. 模式定义

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

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

5. 结构

 

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

6. 要点总结

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

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

  • 如果State对象没有实例变量(即没有后继态之后的实例),那么上下文可以共享一个State对象(代码中的单例),从而节省对象开销。

猜你喜欢

转载自blog.csdn.net/bocai1215/article/details/127660182