C++设计模式_18_State 状态模式

State和Memento被归为"状态变化"模式。

文章目录

  • [1. "状态变化"模式](#1. “状态变化”模式)
    • [1.1 典型模式](#1.1 典型模式)
  • [2. 动机 (Motivation)](#2. 动机 (Motivation))
  • [3. 代码演示State 状态模式](#3. 代码演示State 状态模式)
    • [3.1 常规方式](#3.1 常规方式)
    • [3.2 State 状态模式](#3.2 State 状态模式)
  • [4. 模式定义](#4. 模式定义)
  • [5. 结构( Structure )](#5. 结构( Structure ))
  • [6. 要点总结](#6. 要点总结)
  • [7. 其他参考](#7. 其他参考)

1. "状态变化"模式

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

1.1 典型模式

  • State
  • Memento

2. 动机 (Motivation)

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

3. 代码演示State 状态模式

3.1 常规方式

假设有一个网络的应用,会根据网路状态做一些调整,比如网络有打开,关闭,连接三种状态

cpp 复制代码
enum NetworkState
{
    Network_Open,
    Network_Close,
    Network_Connect,
};

下面的类为网络的应用,NetworkState state;是网络的状态,Operation1()在打开状态Network_Open下具有不同的行为,执行完后,最后状态变为close...;Operation2()则是另外的行为,在打开状态Network_Open下具有不同的行为,执行完后,最后状态变为Network_Connect...。

整体代码如下:

cpp 复制代码
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;
        }
    }

    public 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;
        }
    
    }

    public void Operation3(){

    }
};

动机 (Motivation)中提到的"对象的状态如果改变,其行为也会随之而发生变化",Operation1()中的if...else已经很清楚的表明了Operation1根据状态不同而行为不同。

上面的写法会有什么问题?看到这段代码,大家可能会有似曾相识的感觉,根据上面代码来看和Strategy模式时碰到的问题是一样的,代码中出现了很多的if...else,而且if...else是有关业务状态。Strategy模式告诉我们对于这种情况我们应该问一个为什么?

以下的枚举类型,以后会不会有其他的类型出现?如果添加了一种新的状态,假设增加了Network_Wait,之前的代码如何更改?

cpp 复制代码
enum NetworkState
{
    Network_Open,
    Network_Close,
    Network_Connect,
    Network_Wait,
};

增加了新的状态之后,在不同的operation中需要增加新的if...else。这样的做法显然是违背了开闭原则。即需求的变更导致需要在代码中不断地去改这些地方。

3.2 State 状态模式

上面的做法肯定是不好的,好的做法是参考Strategy模式的经验,先提抽象基类,把枚举类型转换为一个抽象基类

cpp 复制代码
class NetworkState{

public:
    NetworkState* pNext;
    virtual void Operation1()=0;
    virtual void Operation2()=0;
    virtual void Operation3()=0;

    virtual ~NetworkState(){}
};

所有跟状态有关的操作Operation1()等全部变为状态对象的行为,并利用多态的虚函数来进行表达,并塞入一个NetworkState* pNext;状态对象的指针。

创建OpenState,其中使用了前面所讲的单例模式,在状态中倾向使用单例模式,因为状态的对象没必要有多个。此处Operation1()里面省略部分的内容是与上面常规的方式是有差别的,核心逻辑肯定是类似的,经过Operation1()之后就变为close状态,此时pNext = CloseState::getInstance()换的是一个对象,而不是一个枚举。总之是将状态相关的操作,全部编码到一个状态对象中。

如果是在open状态,而且执行的是Operation1()的时候会执行怎样的行为,执行完之后下一个状态会切换为哪个状态。

cpp 复制代码
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(){
        
        //**********
        pNext = CloseState::getInstance();
    }
    
    void Operation2(){
        
        //..........
        pNext = ConnectState::getInstance();
    }
    
    void Operation3(){
        
        //$$$$$$$$$$
        pNext = OpenState::getInstance();
    }
    
    
};

以此类推,得到了close状态等,需要做状态对象。

整个网络应用就改写为状态对象,而不是枚举字段,这种方式与strategy处理的手法是异曲同工。Operation1()中先做收集参数工作,调用pState->Operation1();(虚函数的本质是运行时的if...else,在运行时会判断pState的指针如果指向的是open状态,就会调用open状态的operation1;如果指向的是不同状态就会执行对应状态的operation1),执行完之后就让其等于下一个状态 pState = pState->pNext;,下一个状态是Operation1()内部pNext = ConnectState::getInstance();更改的状态,我本身不用管。

cpp 复制代码
class NetworkProcessor{
    
    NetworkState* pState;
    
public:
    
    //构造器中初始化pState
    NetworkProcessor(NetworkState* pState){
        
        this->pState = pState;
    }
    
    void Operation1(){
        //...
        pState->Operation1();
        pState = pState->pNext;
        //...
    }
    
    void Operation2(){
        //...
        pState->Operation2();
        pState = pState->pNext;
        //...
    }
    
    void Operation3(){
        //...
        pState->Operation3();
        pState = pState->pNext;
        //...
    }

};

上面这样做的好处是与Strategy异曲同工。当状态增加的时候,假如增加一个WaitState,仍然是像上面操作,写WaitState里的operation,而NetworkProcessor里是不用改变的,NetworkProcessor不关心其是什么状态,只关心状态上的行为是对的,状态行为里去自己去管理状态转换关系,是一种扩展的方法。

下为整体代码:

cpp 复制代码
class NetworkState{

public:
    NetworkState* pNext;
    virtual void Operation1()=0;
    virtual void Operation2()=0;
    virtual void Operation3()=0;

    virtual ~NetworkState(){}
};


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(){
        
        //**********
        pNext = CloseState::getInstance();
    }
    
    void Operation2(){
        
        //..........
        pNext = ConnectState::getInstance();
    }
    
    void Operation3(){
        
        //$$$$$$$$$$
        pNext = OpenState::getInstance();
    }
    
    
};

class CloseState:public NetworkState{ }
//...

//扩展
class WaitState:public NetworkState{ }


class NetworkProcessor{
    
    NetworkState* pState;
    
public:
    
    NetworkProcessor(NetworkState* pState){
        
        this->pState = pState;
    }
    
    void Operation1(){
        //...
        pState->Operation1();
        pState = pState->pNext;
        //...
    }
    
    void Operation2(){
        //...
        pState->Operation2();
        pState = pState->pNext;
        //...
    }
    
    void Operation3(){
        //...
        pState->Operation3();
        pState = pState->pNext;
        //...
    }

};

4. 模式定义

允许一个对象在其内部状态改变时改变它的行为。从而使对象看起来似乎修改了其行为。

----《设计模式》GoF

5. 结构( Structure )

上图是《设计模式》GoF中定义的State 状态模式的设计结构。结合上面的代码看图中最重要的是看其中稳定和变化部分,也就是下图中红框和蓝框框选的部分。

State 状态模式和Strategy非常像,State中放的是一个行为,一般是多个行为,一个行为时和Strategy没什么两样的。

6. 要点总结

  • State模式将所有与一个特定状态相关的行为都放入一个State的子类对象中,在对象状态切换时,切换相应的对象;但同时维持State的接口,这样实现了具体操作与状态转换之间的解耦。

  • 为不同的状态引入不同的对象使得状态转换变得更加明确,而且可以保证不会出现状态不一致的情况,因为转换是原子性的--即要么彻底转换过莱,要么不转换

openstate值关心三个操作之后的下一个状态是什么,不需要太多的想更多耦合的情况。相比最初代码实现解耦。

  • 如果state对象没有实例变量,那么各个上下文可以共享同一个State对象,从而节省对象开销

State 状态模式讲下来和Strategy非常像,当最后往回看的时候,你会发现很多模式越来越像,相同点越来越多,学习模式最后会发现,这些模式之间差别会很小,会忘掉具体的模式名称。不一定非要纠结模式的名称,只是松耦合设计原则的演化。你可以将State 状态模式和Strategy看做同一个模式,出现的问题是If...else,枚举,怎么转,利用多态方式实现运行时的改变。掌握了这些,具体是什么模式就没那么重要了。

7. 其他参考

C++设计模式------状态模式

相关推荐
南东山人1 小时前
一文说清:C和C++混合编程
c语言·c++
Ysjt | 深3 小时前
C++多线程编程入门教程(优质版)
java·开发语言·jvm·c++
ephemerals__4 小时前
【c++丨STL】list模拟实现(附源码)
开发语言·c++·list
Microsoft Word4 小时前
c++基础语法
开发语言·c++·算法
一只小小汤圆4 小时前
opencascade源码学习之BRepOffsetAPI包 -BRepOffsetAPI_DraftAngle
c++·学习·opencascade
legend_jz5 小时前
【Linux】线程控制
linux·服务器·开发语言·c++·笔记·学习·学习方法
嘿BRE5 小时前
【C++】几个基本容器的模拟实现(string,vector,list,stack,queue,priority_queue)
c++
ö Constancy6 小时前
c++ 笔记
开发语言·c++
fengbizhe6 小时前
笔试-笔记2
c++·笔记