引语:
我们在编程的时候,有时会遇到一些情况,那就是对象的行为动作会由当时对象所处的状态来决定。例如,我们在微博上看到一篇文章,觉得还不错,于是想评论或者转发,但如果用户没有登录,这个时候就会先自动跳转到登录注册界面,如果已经登录,当然就可以直接评论或者转发了。这里我们可以看到,我们用户的行为是由当前是否登录这个状态来决定的,这就是典型的状态模式情景。
为什么要使用状态模式?
在刚刚上述的情境中,如果我们不用状态模式也是可以的,但这就势必会使用很多if–else–条件判断了,几乎每一个行为动作,你都要去判断一次,例如评论:
if(已经登录){
System.out.print(“评论成功!”)
}else(未登录){
跳到登录界面
}
………………..
当然还包括很多其他动作,例如转发、分享、打赏等等,都要重复判断状态才行,如果程序随着需求的改动或者功能逻辑的增加需要修改代码,那么你只要遗漏了一个判断,就会出问题。
而使用状态模式,可以很好地避免过多的if–else –分支,状态模式将每一个状态分支放入一个独立的类中,每一个状态对象都可以独立存在,程序根据不同的状态使用不同的状态对象来实现功能。
怎样使用状态模式?
下面是状态模式的UML图:
- Context: 环境类,定义客户感兴趣的接口,维护一个State子类的实例,这个实例对应的是对象当前的状态。
- State:抽象状态类或者状态接口,定义一个或者一组行为接口,表示该状态下的行为动作。
- ConcreteStateA、ConcreteStateB: 具体状态类,实现State抽象类中定义的接口方法,从而达到不同状态下的不同行为。
简单示例
下面是一个电视机遥控器为例来演示状态模式的实现。首先电视机的状态有开关两个状态,然后有搜台、上一台、下一台等操作。在电视机关闭的时候,只有开机键是有效的,其他都无效果;在开机的状态下,除了开机键之外,其他的都有效果。我们知道用if–else–来解决这种问题有点显得有点繁琐,下面我们直接用状态模式实现。
/** 电视机状态接口*/
public interface TVState{
public void nextChannel();
public void prevChannel();
public void trunUp();
public void trunDown();
}
/** 关机状态下的具体实现动作类,此时只有开机是有效的*/
public class PowerOffsetState implements TVState{
@override
public void nextChannel(){
}
@override
public void prevChannel(){
}
@override
public void trunUp(){
}
@override
public void trunDown(){
}
}
/** 开机状态的实现*/
public class PowerOnState implements TVState{
@override
public void nextChannel(){
System.out.println("跳转到下一个频道")
}
@override
public void prevChannel(){
System.out.println("跳转到上一个频道")
}
@override
public void trunUp(){
System.out.println("音量增加")
}
@override
public void trunDown(){
System.out.println("音量减少")
}
}
/** 电视遥控器,类似于经典状态模式中的Context*/
public class TVController{
TVState state;
public void setState(TVState state){
this.state = state;
}
public void powerOn(){
setState(new PowerOnState());
System.out.println("开机啦!");
}
public void powerOff(){
setState(new PowerOffState());
System.out.println("关机啦!");
}
public void nextChannel(){
state.nextChannel();
}
public void prevChannel(){
state.prevChannel();
}
public void turnUp(){
state.turnUp();
}
public void trunDown(){
state.trunDown();
}
}
/** 下面是客户端的调用代码*/
public class Client{
public void main(String[] args){
TVController controller = new TVController();
//设置开机状态
controller.powerOn();
//下一个频道
controller.nextChannel();
//音量增加
controller.trunUp();
//关电视机
controller.powerOff();
//上一个频道,此时已经不会有反映了
controller.prevChannel();
}
}
输出结果如下:
开机啦!
跳转到下一个频道
音量增加
关机啦!
总结:
在对象的行动取决于本身的状态时,可以适用于状态模式,免去了过多的if–else判断,这对于一些复杂的和繁琐的判断逻辑有很好的帮助。但是使用状态模式,势必会造成更多的接口和类,对于非常简单的状态判断,可以不使用。