当前位置:C++技术网 > 资讯 > c++设计模式学习--状态模式

c++设计模式学习--状态模式

更新时间:2016-07-20 23:00:23浏览次数:1+次


    状态模式在很多书籍都会涉及到,我也曾经看过,发现很简单,然后就没有实际实现一次。后来想在状态模式下实现《扭蛋机》的时候,发现一些难点,特别是C++这类内存需要自己管理的语言,实现起来时有点困难的。

首先我们看一下状态模式的模型

状态模式的模型

状态类state里面的每一个行为都要传入当前行为,代理类调用它所指向的行为的时候,要传入this(自己),这个传入自己是为了在一个状态里面可以跳跃到另外一个状态。

例如

//当前状态是 状态1
void 行为1(proxy* now)
{
    if(符合状态3条件)
    {
        delete now->_now; 
        now->_now = new 状态3();
        now->_now->行为1();//因为自己没有执行该行为,所以交给状态3
    }
}

我们从代码里面可以看到

delete now->_now; //删除自己
有些同学会问,为什么now->_now就是删除自己?


因为目前的状态是状态1,代理类里面的_now指针是指向状态1的,而执行 void 行为1()的时候就是调用 状态1的行为1,delete 代理类里面_now指针就是delete自己。

状态1->行为1() 这个函数删除了状态1,看起来是不是很难明白?这就对了,难点在于,删除了自己以后,自己的成员函数已经全部释放了,下一步必须交给下一个状态来做这个行为1()

所以下一步必须是


now->_now->行为1();//因为自己没有执行该行为,所以交给状态3

这个就是注意点

千万不要再调用成员变量或者成员函数,否则都是无效或者崩溃。


然而我们看上图的状态模式实现,不难发现,每一个状态都需要传入当前代理,如 vitrual void 行为1(proxy* 当前行为);

这个的参数会令人感到厌倦。所以我们会做个构造函数,在构造的时候就直接获取这个指针


class State
{
 public:
State(proxy* proxy_in):proxy(proxy_in){}
行为1();
行为2();
行为3();
 private:
proxy* _proxy;
}; 


这样我们就不用在每一个成员函数都传进去一个指针了。

然而这个做法是有缺陷的。

这里有同学就起哄了,为什么你刚刚提出了这个方案,又立刻否决了,能不能爽点?

我,我,我,只是想引导你们嘛~~

我们再看一段代码


//当前的状态为 状态1
void 行为1()
{
    if(符合状态3条件)
    {
        delete _proxy->_now; 
        _proxy->_now = new 状态3();
        _proxy->_now->行为1();//因为自己没有执行该行为,所以交给状态3
    }
}


同学们仔细看看,delete _proxy->_now;以后会怎么样?成员变量 _proxy,这个指针还有效吗?

很明显类都被删除了,成员变量被释放,_proxy想当然地变成了野指针,也许碰巧的你再使用还可以。但是理论上或者debug版上做这个动作,_proxy已经变成(0xcdcdcdcd or 0xccccccc)这样了。

那么我们怎么办才能办呢?


 proxy* temp = _proxy;
delete temp->_now; 
 temp->_now = new 状态3();
 temp->_now->行为1();//因为自己没有执行该行为,所以交给状态3
我目前只能想到这样的解决方案了。(如果有更好的方法,请留言);


还是原来的注意点,delete当前状态以后,不要继续使用成员变量了。这个需要维护者小心翼翼的写代码。

然而,具有托管,垃圾回收机制的语言很可能不存在这个问题。


说了这么多,给出我的版本


有一个接口action来规范,代理类只能做代理的作用。state类构造的时候要传入两个参数,一个是代理状态的,一个是要处理的数据。


有些同学就会问了。能不能把状态更换的方法交给代理类或者一个管理者来处理,而不要自己做delete释放工作,这样的话是不是更好?

其实看图的时候我也做了相应的保留,例如setstate的时候把当前状态放到_lastSta指针里,把要更换的新状态放到_sta里,在下一次切换的时候,再删除_lastSta;这样做也许是能防止成员变量被无效的问题,但是也会产生一些新问题,例如明明我上一个状态已经切换了,它还存在呢,我是做了计数的,这样会妨碍到我~~~~之类的问题。


我看到过一些状态模式是 一开始把所有状态都实例化,作为成员变量,然后切换模式的时候就只更改指针,而不进行删除工作,这个也算是一总解决方案,但是状态之间的可控就会降低一些。因为可能会出现未切换状态就调用了其他状态的函数的情况。


希望大家能,动手多实践,学到了东西就分享分享,谢谢各位~~