3、使用环境类实现状态的转换
在状态模式中实现状态转换时,具体状态类可通过调用环境类Context的setState()方法进行状态的转换操作,也可以统一由环境类Context来实现状态的转换。此时,增加新的具体状态类可能需要修改其他具体状态类或者环境类的源代码,否则系统无法转换到新增状态。但是对于客户端来说,无须关心状态类,可以为环境类设置默认的状态类,而将状态的转换工作交给具体状态类或环境类来完成,具体的转换细节对于客户端而言是透明的。
在上面的“智能空调状态转换”实例中,我们通过具体状态类来实现状态的转换。除此之外,我们还可以通过环境类来实现状态转换,环境类作为一个状态管理器,统一实现各种状态之间的转换操作。
下面通过简单实例来说明如何使用环境类实现状态转换:
某软件公司某开发人员欲开发一个屏幕放大镜工具,其具体功能描述如下:用户单击“放大镜”按钮之后屏幕将放大一倍,再点击一次“放大镜”按钮屏幕再放大一倍,第三次点击该按钮后屏幕将还原到默认大小。 |
State为抽象状态、NormalState为正常大小状态、LargerState为放大2倍大小状态、LargestState为放大4倍大小状态。Screen为屏幕类,也就是环境类Contex。
屏幕类声明如下:
//屏幕类
class Screen
{
private:
//当前状态
State * m_pState;
//正常大小,2倍大小,4倍大小状态
State * m_pNormalState;
State * m_pLargerState;
State * m_pLargestState;
public:
Screen();
~Screen();
//屏幕点击事件
void OnClick();
//设置状态
void SetState(State * pState);
};
屏幕类实现代码如下:
//构造函数
Screen::Screen()
{
//创建正常大小、2倍大小、4倍大小对象
m_pNormalState = new NormalState();
m_pLargerState = new LargerState();
m_pLargestState = new LargestState();
//初始状态为正常大小
m_pState = m_pNormalState;
m_pState->Display();
}
//虚构函数
Screen::~Screen()
{
if( NULL != m_pState )
{
delete m_pState;
m_pState = NULL;
}
}
//设置状态
void Screen::SetState(State * pState)
{
m_pState = pState;
}
//屏幕点击事件
void Screen::OnClick()
{
if( m_pState == m_pNormalState )
{
SetState(m_pLargerState);
m_pState->Display();
}
else if( m_pState == m_pLargerState )
{
SetState( m_pLargestState );
m_pState->Display();
}
else if( m_pState == m_pLargestState )
{
SetState(m_pNormalState);
m_pState->Display();
}
}
抽象状态类和具体状态类声明如下:
//抽象状态
class State
{
public:
virtual void Display() = 0;
};
//正常大小状态
class NormalState : public State
{
public:
virtual void Display();
};
//两倍大小状态
class LargerState : public State
{
public:
virtual void Display();
};
//四倍大小状态
class LargestState : public State
{
public:
virtual void Display();
};
抽象状态类和具体状态类实现如下:
void NormalState::Display()
{
printf("正常大小\n");
}
void LargerState::Display()
{
printf("2倍大小\n");
}
void LargestState::Display()
{
printf("4倍大小\n");
}
测试代码实现如下:
#include <stdio.h>
#include "State.h"
int main()
{
Screen * pScreen = new Screen();
pScreen->OnClick();
pScreen->OnClick();
pScreen->OnClick();
delete pScreen;
pScreen = NULL;
return 0;
}
编译并执行,结果如下:
在上述代码中,所有的状态转换操作都由环境类Screen来实现,此时,环境类充当了状态管理器角色。如果需要增加新的状态,例如“八倍状态类”,需要修改环境类,这在一定程度上违背了“开闭原则”,但对其他状态类没有任何影响。这也是软件工程中的一种折中思想。