在现实中,对象的表现往往不是固定的,而是随时间或者环境而变化的,大到人随时间而变老,小到心情随着天气而喜怒哀乐。这时候,对象的状态随着某些因素而发生改变,行为也跟着随之改变,如同雨夜(因素)容易使人伤感(状态)而买醉(行为)。
当一个对象的行为取决于其状态,并且它必须在运行时根据时间状态改变其行为时,我们就能使用状态模式来实现,从而消除大量的switch分支判断。
首先,定义这个行为取决于状态的对象,这里以天气为对象,展示天气在你喜欢的那个她的状态的影响下的改变
class weather{
// 影响状态的因素
public $you;
//状态
public $state;
//初始化环境参数和所处状态
public function __construct($e,$s)
{
$this->you=$e;
$this->state=$s;
}
//设置状态
public function setState(state $s){
$this->state=$s;
}
//设置环境因素
public function setEnvironment($t){
$this->you=$t;
}
//行为
public function motion(){
$this->state->yourstate($this);
}
}
接下来定义一个抽象的状态类,类中定义了一个状态所对应的行为:
abstract class state{
abstract function yourState(weather $weacher);
}
然后定义具体状态:
class state1 extends state {
function yourState(weather $weather)
{
//判断环境和当前状态是否匹配
if('安好' == $weather->you){
echo "便是晴天";
}else{
//不匹配则将flower状态设置为下一状态,并调用motion再次执行该流程
$weather->setState(new state2());
$weather->motion();
}
}
}
class state2 extends state {
function yourState(weather $weather)
{
if('不安好'==$weather->you){
echo "便是阴天<br>";
}else{
$weather->setState(new state1());
$weather->motion();
}
}
}
到目前为止,来看看我们已经定义的东西
weather:行为取决于状态的类
state:抽象状态类
state1,state2:具体状体类
好了,接下来就可以在我们的客户端来使用状态模式了
//假设'你'是'安好'的,初始状态为state1
$obj=new weather('安好',new state1());
//在当前状态下发生行为,天气会是晴天
$obj->motion();
//改变当前环境
$obj->you='不安好';
//再调用行为方法,此时对象的状态会发生变化,使行为发生变化,天气会是阴天
$obj->motion();
总结
可以看出,当我们需要添加新的状态时,只需要定义新的具体状态类即可。相比利用swtich分支对环境因素作出判断从而选择不同的行为,显然状态模式很好地体现了开放封闭原则。