设计模式学习总结:状态模式(State)

来源:互联网 发布:淘宝品质退款包括哪些 编辑:程序博客网 时间:2024/06/06 16:42

1.场景分析

假如我们需要为一家糖果公司制作一个糖果机。糖果机的运行机制如下面的状态图所示:
从图中我们知道,状态机有四个状态:没有25分钱、有25分钱、售出糖果、糖果售罄。所以我们可以通过一个枚举类型来定义每个状态:

    enum class MachineState    {        SOLD_OUT,        NO_QUARTER,        HAS_QUARTER,        SOLD    };

其有四个操作:投入25分、退回25分、转动曲柄、发放糖果,可以分别用四个函数来表示这四种动作:

    void insertQuarter();    void ejectQuarter();    void turnCrank();    void dispense();

最后我们写出来的糖果机如下所示:

    class GumballMachine{public:    enum class MachineState    {        SOLD_OUT,        NO_QUARTER,        HAS_QUARTER,        SOLD    };    void insertQuarter()    {        switch (_state)        {        case MachineState::SOLD_OUT:            cout << "you can not insert a quarter, the machine is sold out" << endl;            break;        case MachineState::NO_QUARTER:            cout << "you insert a quarter" << endl;            _state = MachineState::HAS_QUARTER;            break;        case MachineState::HAS_QUARTER:            cout << "you can not insert another quarter" << endl;            break;        case MachineState::SOLD:            cout << "please wait, we are already giving you a gumball" << endl;            break;        default:            break;        }    }    void ejectQuarter()    {        switch (_state)        {        case MachineState::SOLD_OUT:            cout << "you can not eject, you haven't inserted a quarter" << endl;            break;        case MachineState::NO_QUARTER:            cout << "you haven't inserted a quarter" << endl;            break;        case MachineState::HAS_QUARTER:            cout << "Quarter returned" << endl;            _state = MachineState::HAS_QUARTER;            break;        case MachineState::SOLD:            cout << "sorry, you already turned the crank" << endl;            break;        default:            break;        }    }    void turnCrank()    {        switch (_state)        {        case MachineState::SOLD_OUT:            cout << "you turned, but there are no gumballs" << endl;            break;        case MachineState::NO_QUARTER:            cout << "you turned, but there is no quarter" << endl;            break;        case MachineState::HAS_QUARTER:            cout << "you turned" << endl;            _state = MachineState::SOLD;            dispense();            break;        case MachineState::SOLD:            cout << "turning twice doesn't get you another gumball" << endl;            break;        default:            break;        }    }    void dispense()    {        switch (_state)        {        case MachineState::SOLD_OUT:            cout << "no gumball dispensed" << endl;            break;        case MachineState::NO_QUARTER:            cout << "you need to pay first" << endl;            break;        case MachineState::HAS_QUARTER:            cout << "no gumball dispensed" << endl;            break;        case MachineState::SOLD:            cout << "A gumball come rolling out the slot" << endl;            --_gumballCount;            if (_gumballCount == 0)                _state = MachineState::SOLD_OUT;            else                _state = MachineState::NO_QUARTER;            break;        default:            break;        }    }private:    MachineState _state = MachineState::NO_QUARTER;    int _gumballCount = 0;};

从上述代码中,我们可以看到大量的switch语句,状态管理十分混乱,而且如果新添加一个状态将十分艰难,代码变得难以扩展。这时我们可以通过状态模式来改善我们的代码设计。
首先我们定义一个State基类,在这个类中,糖果机的每个动作都有一个对应的方法。

class MachineState{public:    virtual void insertQuarter(){}    virtual void ejectQuarter(){}    virtual void turnCrank(){}    virtual void dispense(){}};

然后我们为机器的每个状态实现状态类。这些类负责在对应状态下进行机器的行为。
最后,我们要摆脱旧的条件代码,取而代之的方法是将动作委托到状态类。
最后我们重构后的糖果机如下图:

#include <iostream>using namespace std;class MachineState{public:    virtual void insertQuarter(){}    virtual void ejectQuarter(){}    virtual void turnCrank(){}    virtual void dispense(){}};class GumballMachine{public:    void setState(MachineState *state)    {        if (_state)            delete _state;        _state = state;    }    int getGumballCount() { return _gumballCount; }    void releaseGumball() { --_gumballCount; }    void insertQuarter(){ if (!_state) return; _state->insertQuarter(); }    void ejectQuarter(){ if (!_state) return; _state->ejectQuarter(); }    void turnCrank(){ if (!_state) return; _state->turnCrank(); }    void dispense(){ if (!_state) return;  _state->dispense(); }private:    MachineState *_state = nullptr;    int _gumballCount = 0;};class NoQuarterState : public MachineState{public:    NoQuarterState(GumballMachine *machine) :_machine(machine){}    void insertQuarter() override    {        _machine->setState(new HasQuarterState(_machine));    }private:    GumballMachine * _machine = nullptr;};class HasQuarterState : public MachineState{public:    HasQuarterState(GumballMachine *machine) :_machine(machine){}    void ejectQuarter() override    {        _machine->setState(new SoldState(_machine));    }private:    GumballMachine * _machine = nullptr;};class SoldState : public MachineState{public:    SoldState(GumballMachine *machine) :_machine(machine){}    void dispense() override    {        _machine->releaseGumball();        if (_machine->getGumballCount() > 0)            _machine->setState(new NoQuarterState(_machine));        else            _machine->setState(new SoldOutState(_machine));    }private:    GumballMachine * _machine = nullptr;};class SoldOutState : public MachineState{public:    SoldOutState(GumballMachine *machine) :_machine(machine){}private:    GumballMachine * _machine = nullptr;};

2.意图

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

3.适用性

  1. 一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为。
  2. 一个操作中含有庞大的多分支条件语句,且这些分支依赖于对象的状态,这些状态通常用一个或多个枚举常量表示。

4.结构

这里写图片描述
Context:环境,定义客户端感兴趣的接口。
State:环境所处的状态,定义Context与特定状态相关的行为。

5.效果

1>它将与特定状态相关的行为局部化,并且将不同状态的行为分割开。State模式将所有与一个特定状态相关的行为都放入一个对象中。因为所有与状态相关的代码都存在于某一个State子类中,所以通过定义新的子类可以很容易的增加新的状态和装换。这样我们就避免了使用一大堆相似的条件语句来管理状态,但是同时也引入了另一个问题,就是增加了子类的数量,相对于当个类的实现来说不够紧凑。我们让每一个状态“对修改关闭”,而让context“对扩展开放”。
2>它使得状态转换显示化。当一个对象仅以内部数据值来定义当前状态时,其状态仅表现在对一些变量的赋值,这不够明确。为不同的状态引入独立的对象使得装换变得更加明确。
3>State对象可以被共享,如果State对象没有实例变量——即它们表示的状态完全以它们的类型来编码——那么各Context可以共享一个状态对象。当状态以这种方式被共享时,它们必定是没有内部状态而只有行为的轻量级对象(Flyweight)。

6.实现

1>谁定义状态转换。有两种选择,第一种是在Context中实现所有状态转换的操作,另一种是由State子类自身来指定它们的后续状态以及何时进行转换。通常第二种方法更加灵活,但是这样做则一个State子类至少拥有另一个State子类的相关信息,增加了子类之间的耦合性。我们前面例子中采用的第二种实现方法。
2>基于表的另一种方法。
3>创建和销毁State对象。有两种选择:(1)仅当需要State对象时才创建它们并在使用后销毁。(2)提前创建它们并且始终不销毁它们。当上下文不经常改变状态时,第一种比较适合,如果状态改变很频繁,则第二种比较合适。

7.代码示例

我们来实现一个表示网络连接的类TCPConnection,并用状态模式来实现其处于不同的网络状态。

#include <iostream>using namespace std;class TCPConnection;class TCPState{public:    virtual void activeOpen(TCPConnection *tcpConnection){}    virtual void passiveOpen(TCPConnection *tcpConnection){}    virtual void close(TCPConnection *tcpConnection){}    virtual void send(TCPConnection *tcpConnection){}    virtual void acknowledge(TCPConnection *tcpConnection){}    virtual void synchronize(TCPConnection *tcpConnection){}protected:    void changeState(TCPConnection *tcpConnection, TCPState *tcpState);};class TCPCloseState : public TCPState{public:    static TCPCloseState *create()    {        return new TCPCloseState();    }    void activeOpen(TCPConnection *tcpConnection);};class TCPOpenState : public TCPState{public:    static TCPOpenState *create()    {        return new TCPOpenState();    }    //省略其他状态转化实现    ...};class TCPConnection{public:    TCPConnection()    {        _state = TCPCloseState::create();    }    void activeOpen() { if (!_state) return; _state->activeOpen(this); }    void passiveOpen() { if (!_state) return; _state->passiveOpen(this); }    void close() { if (!_state) return; _state->close(this); }    void send() { if (!_state) return; _state->send(this); }    void acknowledge() { if (!_state) return; _state->acknowledge(this); }    void synchronize() { if (!_state) return; _state->synchronize(this); }private:    friend TCPState;    void changeState(TCPState *state);    TCPState *_state = nullptr;};void TCPState::changeState(TCPConnection *tcpConnection, TCPState *tcpState){    tcpConnection->changeState(tcpState);}void TCPCloseState::activeOpen(TCPConnection *tcpConnection){    changeState(tcpConnection, TCPOpenState::create());}
原创粉丝点击