一.概念
状态模式:当一个对象的内在状态改变时允许改变其行为,看起来就像是修改这个类。
二.UML
- Context(应用场景),持有State对象的引用。不管在任何时候,只有有人调用Context的request方法,它就会被委托到对应的状态来处理。
- State(具体对象的共同接口),任何状态实现类都实现这一接口,做到状态之间的互相切换。
- ConcreteStrategryA-C(状态实现类),ConcreteState处理来自Context的请求,每个ConcreteState都提供了它自己对于请求的实现。所以,当Context改变状态时行为也跟着改变。即状态决定行为。
三.实例分析
上次回家把驾照给拿了。科目一二去年就过了,这次就考了科目三(路考)。先说点科目三相关的知识,科目三要在1500M内从1挡到5挡,每档有时间速度限制;其它的知识的一些与状态模式无关就不谈。因为工作的原因,没时间学,找了点关系,请人吃了饭,送了礼。最后考试的时候,我就上了个车意思了下,什么都没做,车子开完1500M我就下车了,都不知道这1档到5档是怎么跑下来的...重点来了:我作为一个路考着,对1档到5档这些状态的变化浑然不觉。又一个马路杀手来了啊。
从这个里面,我们可以抽象出这样的几个类:
Gears1-5:具体档位
RoadRiver:路考者
package com.zzy.state;
/**
* 路考者
* 相当于Context
* @author eason
*
*/
public class RoadRiver {
//路考者持有档位这个对象
private Gears gears;
public RoadRiver(Gears gears) {
this.gears = gears;
}
//路考着开车
public void drive() {
gears.drive(this);
}
public void setGears(Gears gears) {
this.gears = gears;
}
}
Gears:档位接口
package com.zzy.state;
public interface Gears {
public void drive(RoadRiver rd);
}
Gears0:档位0。 Gears1-5同Gears0,在drive方法里面切换到下一档位。
package com.zzy.state;
/**
* 档位0
* @author eason
*
*/
public class Gears0 implements Gears{
public void drive(RoadRiver rd) {
System.out.println("准备起步");
//这里就是状态模式的核心了
//从0切换到1挡,在此完成
rd.setGears(new Gears1());
}
}
RoadDriverTest
package com.zzy.state;
/**
* 测试类
* @author eason
*
*/
public class RoadDiverTest {
public static void main(String[] args) {
Gears gears0 = new Gears0();
//路考着从0档开始
RoadRiver rd = new RoadRiver(gears0);
//这里就是状态模式的核心了
//路考着看似什么都没做,实际上已经从0切换到1挡了
rd.drive();
rd.drive();
rd.drive();
rd.drive();
rd.drive();
rd.drive();
}
}
四.实例分析条件控制代码
Gears
package com.zzy.state.base;
/**
* 档位
* 0-5挡
* @author eason
*
*/
public enum Gears {
G0, G1, G2, G3, G4, G5
}
RoadDiver 最好用switch case啦
package com.zzy.state.base;
/**
* 路考者
* @author eason
*
*/
public class RoadDiver {
private Gears gears;
public RoadDiver(Gears gears) {
this.gears = gears;
}
public void setGears(Gears gears) {
this.gears = gears;
}
public void drive() {
if(gears == Gears.G0) {
System.out.println("准备起步");
}if(gears == Gears.G1) {
System.out.println("1挡行驶中,速度15码左右,在20米内换成2挡");
}else if(gears == Gears.G2) {
System.out.println("2挡行驶中,速度25码左右,在50米内换成3挡");
}else if(gears == Gears.G3) {
System.out.println("3挡行驶中,速度35码左右,保持直线行驶3秒");
}else if(gears == Gears.G4) {
System.out.println("4挡行驶中,速度45码左右");
}else if(gears == Gears.G5) {
System.out.println("5挡行驶中,请保持50码到60码行驶3秒");
}
}
}
RoadDriverTest
package com.zzy.state.base;
public class RoadDiverTest {
public static void main(String[] args) {
RoadDiver rd = new RoadDiver(Gears.G0);
rd.drive();
rd.setGears(Gears.G1);
rd.drive();
rd.setGears(Gears.G2);
rd.drive();
rd.setGears(Gears.G3);
rd.drive();
rd.setGears(Gears.G4);
rd.drive();
rd.setGears(Gears.G5);
rd.drive();
}
}
五.使用场景及使用感受
- 将特定的状态相关的行为都放入一个对象中,由于所有与状态相关的代码都存在于某个ConcreteState中,所有通过定义新的子类都可以很容易地增加新的状态和转换。
- 消除了庞大的条件分支语句。将一个个状态封装变成一个个ConcreteState,并将动作委托到代表当前状态的对象。
- 当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态来改变它的行为时,考虑状态模式。
- ConcreteState总是决定接下来的状态是什么吗?不一定。Context也可以决定状态转换的流向。一般来说,当状态转换是固定的时候,适合放在Context中;然而,当状态转换是固定的时候,就通常放在状态类中。
六.状态模式与策略模式
- 都是利用多态把一些操作分配到一组类中。
- 状态模式是完全封装且自修改的策略模式。
- 状态模式:Context的行为随时可以委托到那些状态对象中的一个,这些委托往往发生在Context内部。使用Context的客户对这些状态的转变了解不多,甚至是浑然不觉的。
- 策略模式:客户通常主动指定Context需要的策略。固然策略模式可以在运行时改变策略,当对于某个context对象来说,通常都只有一个最适合的策略对象。