State:状态模式

      某个对象有一个标准接口。同时,该对象可能处于各种状态下。当该对象处于不同状态下时,客户调用标准接口,该对象会产生不同的响应。

      即允许一个对象在其内部状态改变时改变它的行为,使对象看起来似乎修改了它的类。这就是状态模式。

      例如,电灯开关这个对象提供了一个标准接口:按下开关。而开关可能处于关闭/打开这两种状态。当开关处于关闭状态时,调用标准接口按下开关,电灯会被打开;当开关处于打开状态时,调用标准接口按下开关,电灯会被关闭。

      即当开关处于不同状态时,对其执行相同的操作,返回的结果也不同。

 

      状态模式需要定义如下对象:

①   Context:环境,即上面例子中的开关对象。用于定义提供给客户的标准接口。

Context内部会存储一个State指针,用以保存当前状态。

②   State:状态基类,定义一组所有状态所影响到的接口。具体实现延迟到子类。

注意State所定义的这些接口实际上是从Context中分离出的功能。

若将对State的判断及执行对应效果都放在Context中,Context会非常臃肿,且结构不清晰。对State进行修改时也非常麻烦。

所以将Context与状态相关的功能都分离出来,并将这些功能延迟到State的子类进行实现。这样,当Context需要执行某个功能时,调用State成员变量来执行即可。

③    ConcreteState:状态派生类,有多个,针对于Context的每一种状态,都有一个对应的ConcreteState类。实现了State中的相关接口。

 

这样,用户每次直接与Context进行交互,然后Context会调用自身的State成员变量指针来执行指定的效果。

 

还是使用上面电灯开关的例子。电灯开关即Context,含一个标准接口:按下开关。电灯的关闭/打开状态为两个State:ConcreteStateClose,ConcreteStateOpen。其中,State含有两个效果函数:打开电灯/关闭电灯,以及一个接口函数:按下开关。电灯默认状态为关闭状态。

当用户调用Context的按下开关接口时,Context会调用当前状态ConcreteStateClose的按下开关接口,从而ConcreteStateClose将令电灯打开;

当用户再次调用Context的按下开关接口时,Context会调用当前状态ConcreteStateOpen的按下开关接口,从而ConcreteStateOpen将令电灯关闭。

 

 

1.    State基类及派生类

State基类定义了标准接口。该标准接口是从Context中分离出的功能。

State派生类实现接口,根据自身的不同,而有不同的实现。

注意下面的代码将Context的指针内嵌到了State派生类中。这是为了State执行某些与Context相关的操作时方便调用Context的接口。如果State执行的功能相对独立,不需要与Context进行交互,那么可以不用将Context指针嵌入。

//预定义Context类
class Context;

//状态基类
class State
{
public:
	virtual void operation() = 0;
};

//状态派生类
class ConcreteStateA : public State
{
public:
	ConcreteStateA(Context* pContext) : _pContext(pContext) {}

	virtual void operation()
	{
		std::cout << "ConcreteStateA::operation()" << std::endl;
	}
private:
	Context* _pContext;
};

//状态派生类
class ConcreteStateB : public State
{
public:
	ConcreteStateB(Context* pContext) : _pContext(pContext) {}

	virtual void operation()
	{
		std::cout << "ConcreteStateB::operation()" << std::endl;
	}

private:
	Context* _pContext;
};

2.    环境类

环境类将所有类型的状态都内嵌到自身,并维护一个当前状态指针。当改变状态时,直接修改当前状态指针即可。

      当调用环境类的标准接口时,环境类会调用当前状态的标准接口,从而用户间接调用了状态类的标准接口。最终功能由状态类实现。

//环境类
class Context
{
public:
	Context()
	{
		_pConcreteStateA = new ConcreteStateA(this);
		_pConcreteStateB = new ConcreteStateB(this);
		_pCurrentState = _pConcreteStateA;
	}

	void enterStateA() { _pCurrentState = _pConcreteStateB; }
	void enterStateB() { _pCurrentState = _pConcreteStateA; }

	void operation() { _pCurrentState->operation(); }

private:
	void setState(State* pContextMode) { _pCurrentState = pContextMode; }
private:
	ConcreteStateB* _pConcreteStateB;
	ConcreteStateA* _pConcreteStateA;
	State* _pCurrentState;
};

3.    用户使用

由于各种状态由环境类负责创建,所以用户只需要创建并操作环境类即可。

void main()
{
	Context context;
	context.enterStateA();
	context.operation();

	context.enterStateB();
	context.operation();
}

注意上面 Context进入某种状态是由用户来调用的。根据实际需求不同,可以将状态的转换封装到 Context类中,这样用户只需要调用标准接口,状态转换由 Context内部负责。

例如前面的电灯开关例子,开关状态的转换就应该由开关类负责,而非用户。

 

状态模式将所有状态进行了分离,减少了耦合;同时将与特定状态相关的行为局部化;通过定义State派生类,可以很容易地添加新的状态并进行转换。

但是状态模式必然会增加系统类和对象的个数。使用不当有可能会导致结构的混乱。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值