State的定义: 不同的状态不同的行为;或者说每个状态有着相应的行为
何时使用?
State模式在实际使用中比较多适合状态的切换因为我们经常会使用If elseif else 进行状态切换 如果针对状态的这样判断切换反复出现我们就要联想到是否可以采取State模式了
不只是根据状态也有根据属性如果某个对象的属性不同对象的行为就不一样这点在数据库系统中出现频率比较高我们经常会在一个数据表的尾部加上property属性含义的字段用以标识记录中一些特殊性质的记录这种属性的改变(切换)又是随时可能发生的就有可能要使用State
是否使用?
在实际使用类似开关一样的状态切换是很多的但有时并不是那么明显取决于你的经验和对系统的理解深度
这里要阐述的是开关切换状态 和 一般的状态判断是有一些区别的 一般的状态判断也是有 ifelseif结构例如:
if (which==) state=hello;
else if (which==) state=hi;
else if (which==) state=bye;
这是一个 一般的状态判断state值的不同是根据which变量来决定的which和state没有关系如果改成:
if (stateeuqals(bye)) state=hello;
else if (stateeuqals(hello)) state=hi;
else if (stateeuqals(hi)) state=bye;
这就是 开关切换状态是将state的状态从hello切换到hi再切换到bye;在切换到hello好象一个旋转开关这种状态改变就可以使用State模式了
如果单纯有上面一种将hello>hi>bye>hello这一个方向切换也不一定需要使用State模式因为State模式会建立很多子类复杂化但是如果又发生另外一个行为:将上面的切换方向反过来切换或者需要任意切换就需要State了
请看下例:
public class Context{
private Color state=null;
public void push(){
//如果当前red状态 就切换到blue
if (state==Colorred) state=Colorblue;
//如果当前blue状态 就切换到green
else if (state==Colorblue) state=Colorgreen;
//如果当前black状态 就切换到red
else if (state==Colorblack) state=Colorred;
//如果当前green状态 就切换到black
else if (state==Colorgreen) state=Colorblack;
Sample sample=new Sample(state);
sampleoperate();
}
public void pull(){
//与push状态切换正好相反
if (state==Colorgreen) state=Colorblue;
else if (state==Colorblack) state=Colorgreen;
else if (state==Colorblue) state=Colorred;
else if (state==Colorred) state=Colorblack;
Sample sample=new Sample(state);
sampleoperate();
}
}
在上例中我们有两个动作push推和pull拉这两个开关动作改变了Context颜色至此我们就需要使用State模式优化它
另外注意:但就上例state的变化只是简单的颜色赋值这个具体行为是很简单的State适合巨大的具体行为因此在就本例实际使用中也不一定非要使用State模式这会增加子类的数目简单的变复杂
例如: 银行帐户 经常会在Open 状态和Close状态间转换
例如: 经典的TcpConnection Tcp的状态有创建 侦听 关闭三个并且反复转换其创建 侦听 关闭的具体行为不是简单一两句就能完成的适合使用State
例如:信箱POP帐号 会有四种状态 start HaveUsername Authorized quit每个状态对应的行为应该是比较大的适合使用State
例如:在工具箱挑选不同工具可以看成在不同工具中切换适合使用State如 具体绘图程序用户可以选择不同工具绘制方框 直线 曲线这种状态切换可以使用State
如何使用?
State需要两种类型实体参与:
state manager 状态管理器 就是开关 如上面例子的Context实际就是一个state manager 在state manager中有对状态的切换动作
用抽象类或接口实现的父类不同状态就是继承这个父类的不同子类
以上面的Context为例我们要修改它建立两个类型的实体
第一步: 首先建立一个父类:
public abstract class State{
public abstract void handlepush(Context c);
public abstract void handlepull(Context c);
public abstract void getcolor();
}
父类中的方法要对应state manager中的开关行为在state manager中 本例就是Context中有两个开关动作push推和pull拉那么在状态父类中就要有具体处理这两个动作:handlepush() handlepull(); 同时还需要一个获取push或pull结果的方法getcolor()
下面是具体子类的实现:
public class BlueState extends State{
public void handlepush(Context c){
//根据push方法如果是blue状态的切换到green
csetState(new GreenState());
}
public void handlepull(Context c){
//根据pull方法如果是blue状态的切换到red
csetState(new RedState());
}
public abstract void getcolor(){ return (Colorblue)}
}
同样 其他状态的子类实现如blue一样
第二步: 要重新改写State manager 也就是本例的Context:
public class Context{
private Sate state=null; //我们将原来的 Color state 改成了新建的State state;
//setState是用来改变state的状态 使用setState实现状态的切换
pulic void setState(State state){
thisstate=state;
}
public void push(){
//状态的切换的细节部分在本例中是颜色的变化已经封装在子类的handlepush中实现这里无需关心
statehandlepush(this);
//因为sample要使用state中的一个切换结果使用getColor()
Sample sample=new Sample(stategetColor());
sampleoperate();
}
public void pull(){
statehandlepull(this);
Sample sample=new Sample(stategetColor());
sampleoperate();
}
}
至此我们也就实现了State的refactorying过程
以上只是相当简单的一个实例在实际应用中handlepush或handelpull的处理是复杂的