首页 » 开发 » 状态模式

状态模式介绍

GoF 5.8节介绍了状态(State)模式。目的是:允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。GoF 5.8节用了TCP状态转移的例子。

ASD 29.3节介绍了状态模式,并列举了一个加/解锁的例子。

装饰器模式实例 - 游戏中的行为状态

说明

以下是为游戏的行为设计状态。角色有跑动和飞行两种状态,两种状态可以相互切换,切换行动状态后,就有不同的行动动作。

C++代码

定义角色,角色有两种行为(run和fly),并包含一个状态:

class Role
{
    private:
        class State* _state;
    public:
        void set_state(State* s) { _state = s; }
        void run();
        void fly();
};

定义状态基类,并把角色的行为委托给状态类来实现:

class State
{
    public:
        virtual void run(Role* r) { cout << " running "; }
        virtual void fly(Role* r) { cout << " flying "; }
};

void Role::run() { _state->run(this); }
void Role::fly() { _state->fly(this); }

定义具体的状态类:

class Run : public State
{
    public:
        virtual void fly(Role* r);
};

class Fly : public State
{
    public:
        virtual void run(Role* r);
};

void Run::fly(Role* r)
{
    cout << " SWITCH run to fly ";
    r->set_state(new Fly());
    delete this;
}

void Fly::run(Role* r)
{
    cout << " SWITCH fly to run ";
    r->set_state(new Run());
    delete this;
}

测试:

int main()
{
    Role* role = new Role();
    role->set_state(new Run());

    role->run();
    role->fly();
    role->fly();
    role->run();

    return 0;
}

程序输出:

running
SWITCH run to fly
flying
SWITCH fly to run

角色先处于跑步状态,继而切换到飞行,从飞行切换到飞行时什么都不做(执行状态基类的默认动作),继而切换到跑步状态。

对比状态模式和其他设计模式

State模式的好处在于彻底分离了状态机的逻辑和动作。动作在Context类中实现,而逻辑则分散在State类的派生类中,这样两者可独立变化,互不影响。另外,State模式有表驱动的灵活性,也有switch/case方法的效率。

State模式的代价是:State派生类的编写非常乏味;而逻辑分散在State派生类中,无法在一个地方看到整个状态机逻辑。

ASD的作者Robert C. Martin在29.3.1中提供了一个免费的状态机编译器(SMC),可以把一个用文本描述的状态迁移表变成State模式所需的类。

State模式和Strategy模式

相同处:两个模式都有一个上下文,都委托给一个具有几个派生类的多态基类。

在State模式中,派生类持有回指向上下文类的引用,派生类的主要功能是使用这个引用选择并调用上下文类中的方法。如在游戏状态这个例子中,Run和Fly这两个派生类,都调用了上下文类(Role类)的set_state()方法。

Strategy模式中,派生类不必持有指向上下文类的引用,也不需要调用上下文类的方法。因此,所有的State模式同样也是Strategy模式实例,但并非所有的Strategy模式实例都是State模式实例。

分享

0