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

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());
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值