概述:
看看我们平时用的开关,同样一个开关他有2种状态:开和关,当她处于不同的状态的时候她的行为是不一样的,比如当她是开着的时候,你按她一下,她就变成了关闭状态,她是关着的时候按她一下,她就变成了开着的状态。看上去就像是改变了它的类一样,其实我们开发者都知道,我们里面用到了if-else,但是当碰到更多状态时就会造成很多很多if-else,设计和维护就相当的复杂,我们将要学习的状态模式就是允许一个对象在其内部状态改变时改变它的行为,使对象看起来似乎修改了它的类。
类图和实例:
上下文环境(Context):它定义了客户程序需要的接口并维护一个具体状态角色的实例,将与状态相关的操作委托给当前的Concrete State对象来处理。
抽象状态(State):定义一个接口以封装使用上下文环境的的一个特定状态相关的行为。
具体状态(Concrete State):实现抽象状态定义的接口。
这里我们举星际争霸里的坦克作为例子,它不架起来的时候可以攻击,可以移动。架起来的时候攻击增强,但是不能移动:
#include <iostream>
class SiegeTank;
class ISiegeTankState
{
public:
virtual void move(int x, int y) = 0;
virtual void attack() = 0;
};
class SiegeState : public ISiegeTankState
{
public:
SiegeState(SiegeTank* pTank): m_pTank(pTank){}
virtual void move(int x, int y)
{
std::cout << "Can't move in siege mode." << std::endl;
}
virtual void attack()
{
std::cout << "Attacking for 40" << std::endl;
}
private:
SiegeTank* m_pTank;
};
class TankState : public ISiegeTankState
{
public:
TankState(SiegeTank* pTank): m_pTank(pTank){}
virtual void move(int x, int y)
{
std::cout << "Move to (" << x << ", " << y << ")" << std::endl;
}
virtual void attack()
{
std::cout << "Attacking for 20" << std::endl;
}
private:
SiegeTank* m_pTank;
};
class SiegeTank
{
public:
SiegeTank()
{
m_pTankState = new TankState(this);
m_pSiegeState = new SiegeState(this);
m_pSiegeTankState = m_pTankState;
}
void enterTankMode()
{
m_pSiegeTankState = m_pTankState;
std::cout << "Switch to tank mode" << std::endl;
}
void enterSiegeMode()
{
m_pSiegeTankState = m_pSiegeState;
std::cout << "Switch to siege mode" << std::endl;
}
public:
void attack()
{
m_pSiegeTankState->attack();
}
void move(int x, int y)
{
m_pSiegeTankState->move(x, y);
}
private:
void setState(ISiegeTankState* pSiegeTankMode)
{
m_pSiegeTankState = pSiegeTankMode;
}
private:
TankState* m_pTankState;
SiegeState* m_pSiegeState;
ISiegeTankState* m_pSiegeTankState;
};
int main()
{
SiegeTank tank;
tank.enterTankMode();
tank.attack();
tank.move(1, 1);
tank.enterSiegeMode();
tank.attack();
tank.move(2, 2);
tank.enterTankMode();
tank.attack();
tank.move(3, 3);
return 0;
} |