0x1、定义
原始定义
允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了自己的类一样。
简单点说
让一个对象通过一系列状态的变化来控制行为的变化。
状态模式
和 策略模式
极其相似,可通过内在差别进行区分:
- 策略模式将具体策略类暴露出去,调用者需了解每种策略的不同之处以便正确使用,封装的是不同算法,算法间没有交互,以达到算法可以自由切换的目的。
- 状态模式状态的改变是由其内部条件来改变的,与外界无关,封装的是不同状态,以达到状态切换行为随之切换的目的。
0x2、写个简单例子
有home键的Android机为例,按下home键,处于不同状态有不同的行为:
- 关机状态 → 没有反应;
- 开机后首次启动 → 密码解锁;
- 非首次启动 → 密码解锁或指纹解锁;
- 启动后 → 返回主界面
不使用状态模式实现一波:
public class StateTest { private static int state = 0; private final static int CLOSE = 0; // 关机状态 private final static int FIRST_BOOT = 1; // 首次启动 private final static int NOT_FIRST_BOOT = 2; // 非首次启动 private final static int AFTER_BOOT = 3; // 启动后 private static void clickHome() { if(state == CLOSE) { System.out.println("处于关机状态,没有反应"); } else if(state == FIRST_BOOT) { System.out.println("首次启动。可以进行密码解锁"); } else if(state == NOT_FIRST_BOOT) { System.out.println("非首次启动,可以进行密码或指纹解锁"); } else if(state == AFTER_BOOT) { System.out.println("启动状态,返回主界面"); } } public static void main(String[] args) { state = CLOSE; clickHome(); state = FIRST_BOOT; clickHome(); state = NOT_FIRST_BOOT; clickHome(); state = AFTER_BOOT; clickHome(); } }
代码运行结果如下:
网络异常,图片无法展示
|
如果需要增加一种状态,如处于fastboot模式,状态定义要写一个,然后if-else加一个判断;还有,不止处理Home键,还有音量键、电源键,又得定义几个函数,然后复制一波这个if-else,试试用状态模式实现一波。
// 抽象状态 public abstract class State { protected StateContext context; public void setContext(StateContext context) { this.context = context; } abstract void onHomeClick(); abstract void onPowerClick(); abstract void onVolumeAscClick(); abstract void onVolumeDescClick(); } // 具体状态 → 关机状态 public class CloseState extends State { @Override public void onHomeClick() { System.out.println("处于关机状态,按Home键没有反应"); } @Override void onPowerClick() { System.out.println("手机开机"); context.setState(FirstBootState.class); context.setScreenOn(true); context.getState().onHomeClick(); } @Override void onVolumeAscClick() { System.out.println("处于关机状态,按音量+没反应"); } @Override void onVolumeDescClick() { System.out.println("处于关机状态,按音量-没反应"); } } // 具体状态 → 第一次启动状态 public class FirstBootState extends State { @Override public void onHomeClick() { System.out.println("首次启动,可以进行密码解锁"); System.out.println("解锁完毕,进入主界面"); context.setState(AfterBootState.class); context.setScreenOn(true); } @Override void onPowerClick() { if(context.isScreenOn()) { System.out.println("熄屏"); } else { System.out.println("亮屏,等待密码解锁"); } context.setScreenOn(!context.isScreenOn()); } @Override void onVolumeAscClick() { System.out.println("音量+"); } @Override void onVolumeDescClick() { System.out.println("音量-"); } } // 具体状态 → 非第一次启动状态 public class NotFirstBootState extends State { @Override public void onHomeClick() { System.out.println("非首次启动,可以通过密码或指纹解锁"); System.out.println("解锁完毕,进入主界面"); context.setScreenOn(true); context.setState(AfterBootState.class); } @Override void onPowerClick() { if(context.isScreenOn()) { System.out.println("熄屏"); } else { System.out.println("亮屏,等待密码或指纹解锁"); context.setState(NotFirstBootState.class); } context.setScreenOn(!context.isScreenOn()); } @Override void onVolumeAscClick() { System.out.println("音量+"); } @Override void onVolumeDescClick() { System.out.println("音量-"); } } // 具体状态 → 启动后 public class AfterBootState extends State { @Override void onHomeClick() { System.out.println("返回主界面"); } @Override void onPowerClick() { if(context.isScreenOn()) { System.out.println("熄屏"); context.setState(NotFirstBootState.class); } else { System.out.println("亮屏,等待密码或指纹解锁"); context.getState().onHomeClick(); } context.setScreenOn(!context.isScreenOn()); } @Override void onVolumeAscClick() { System.out.println("音量+"); } @Override void onVolumeDescClick() { System.out.println("音量-"); } } // 上下文信息类 public class StateContext { private boolean isScreenOn = false; // 屏幕是否亮着 public final static Map<Class, State> stateMap = new HashMap<>(); private State state; // 手机当前状态 static { stateMap.put(CloseState.class, new CloseState()); stateMap.put(FirstBootState.class, new FirstBootState()); stateMap.put(NotFirstBootState.class, new NotFirstBootState()); stateMap.put(AfterBootState.class, new AfterBootState()); } public void setState(Class stateClass) { this.state = stateMap.get(stateClass); this.state.setContext(this); } public State getState() { return state; } public boolean isScreenOn() { return isScreenOn; } public void setScreenOn(boolean screenOn) { isScreenOn = screenOn; System.out.println("===> 屏幕处于:" + (isScreenOn ? "亮屏状态": "熄屏状态")); } } // 测试用例 public class StateTest { public static void main(String[] args) { StateContext context = new StateContext(); context.setState(CloseState.class); // 处于关机状态点击音量- 和 home键 context.getState().onVolumeDescClick(); context.getState().onHomeClick(); // 处于关机状态点击电源键 context.getState().onPowerClick(); context.getState().onPowerClick(); context.getState().onHomeClick(); context.getState().onVolumeAscClick(); } }