状态模式(State):当一个对象内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。
一、状态模式
在软件开发中,有很多情况是一个对象的行为取决于一个或多个动态变化的属性,这样的属性叫做状态,这样的对象叫做有状态的对象,这样的对象从实现定义好的一系列值中取出。当一个这样的对象与外部事件产生互动时,其内部状态就会发生改变,从而使得系统的行为发生变化。
状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同的一系列类当中,可以把复杂的逻辑判断简单化。其UML图如下:
状态模式由以下部分组成:
上下文环境(Context):它定义了客户程序需要的接口并维护一个具体状态角色的实例,这个实例定义当前的状态。
抽象状态(State):定义一个接口以封装使用Context的一个特定状态相关的行为。
具体状态(ConcreteState):每一个子类实现一个与Context的一个状态相关的行为。
二、案例分析
我们看《西游记》中,唐僧四人历经九九八十一难,终于登上灵山,取得真经。书中记载:
金蝉遭贬第一难,出胎几杀第二难, 满月抛江第三难,寻亲报冤第四难,
出城逢虎第五难,落坑折从第六难, 双叉岭上第七难,两界山头第八难,
陡涧换马第九难,夜被火烧第十难, 失却袈裟十一难,收降八戒十二难,
黄风怪阻十三难,请求灵吉十四难, 流沙难渡十五难,收得沙僧十六难,
四圣显化十七难,五庄观中十八难, 难活人参十九难,贬退心猿二十难,
黑松林失散二十一难,宝象国捎书二十二难, 金銮殿变虎二十三难,平顶山逢魔二十四难,
莲花洞高悬二十五难,乌鸡国救主二十六难, 被魔化身二十七难,号山逢怪二十八难,
风摄圣僧二十九难,心猿遭害三十难, 请圣降妖三十一难,黑河沉没三十二难,
搬运车迟三十三难,大赌输赢三十四难,祛道兴僧三十五难,路逢大水三十六难,
身落天河三十七难,鱼篮现身三十八难,金山遇怪三十九难,普天神难伏四十难,
问佛根源四十一难,吃水遭毒四十二难,西梁国留婚四十三难,琵琶洞受苦四十四难,
再贬心猿四十五难,难辨猕猴四十六难,路阻火焰山四十七难,求取芭蕉扇四十八难,
收缚魔王四十九难,赛城扫塔五十难,取宝救僧五十一难,棘林吟咏五十二难,
小雷音遇难五十三难,诸天神遭困五十四难,稀柿衕秽阻五十五难,朱紫国行医五十六难,
拯救疲癃五十七难,降妖取后五十八难,七情迷没五十九难,多目遭伤六十难,
路阻狮驼六十一难,怪分三色六十二难,城里遇灾六十三难,请佛收魔六十四难,
比丘救子六十五难,辨认真邪六十六难,松林救怪六十七难,僧房卧病六十八难,
无底洞遭困六十九难,灭法国难行七十难,隐雾山遇魔七十一难,凤仙郡求雨七十二难,
失落兵器七十三难,会庆钉钯七十四难,竹节山遭难七十五难,玄英洞受苦七十六难,
赶捉犀牛七十七难,天竺招婚七十八难,铜台府监禁七十九难,凌云渡脱胎八十难,路经十万八千里,圣僧历难簿分明。”
之所以大废篇章叙述这么详细,是因为这九九八十一难,简直就是一路升级打怪之路啊,这就是个游戏啊。每一关都是观音菩萨精心设计,过了一关就有下一关,直到:
菩萨将难簿目过了一遍,急传声道:“佛门中九九归真,圣僧受过八十难,还少一难,不得完成此数。”
这才完成了取经之路。这就是个状态模式。其UML图如下:
代码部分:
抽象状态类:
abstract class State{
public abstract void handle(Context context);
}
具体状态类:
class ConcreteStateA extends State{
@Override
public void handle(Context context) {
System.out.println("金蝉遭贬第一难,");
context.setState(new ConcreteStateB());
}
}
class ConcreteStateB extends State{
@Override
public void handle(Context context) {
System.out.println("出胎几杀第二难,");
context.setState(new ConcreteStateC());
}
}
class ConcreteStateC extends State{
@Override
public void handle(Context context) {
System.out.println("满月抛江第三难,");
context.setState(new ConcreteStateD());
}
}
class ConcreteStateD extends State{
@Override
public void handle(Context context) {
System.out.println("寻亲报冤第四难, ");
context.setState(new ConcreteStateA());
}
}
Context:
class Context{
private State state;
public Context(State state) {
this.state = state;
}
public void Request(){
state.handle(this);
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
}
故事上演:
public class Story {
public static void main(String[] args) {
Context c= new Context(new ConcreteStateA());
c.Request();
c.Request();
c.Request();
c.Request();
c.Request();
c.Request();
}
}
运行结果(代码亲测可以通过):
代码地址:State
三、模式结语
在上面的例子中,实际的状态只列举了四种,但我在故事中,调用了6次改变状态的方法,说明以上的列子不太适合状态模式,但状态模式的重点是允许一个对象基于内部状态而拥有不同的行为。
欢迎大家留言评论,点击查看更多设计模式。