1. 什么是状态模式?
《Head First设计模式》中定义:允许对象在内部状态改变时改变他的行为,对象看起来就像是修改了他的类。状态模式是对象的行为模式。
这个模式将状态封装成独立的类,将行为委托到代表当前状态的对象里面,这样行为就会随着内部状态改变而改变。
状态模式允许一个对象基于内部状态而拥有不同的行为。
对象的状态通常指的是对象的属性值。而行为指对象的功能,可以具体对应到对象的方法上,每一个方法就是一种行为。状态决定行为。
状态模式的意图就是在对象内部状态发生改变的时候,其行为也随之改变。
2. 角色
图片来源于《Head First设计模式》
环境(Context):拥有一些内部状态实例,当状态改变的时候可以改变相应行为。
抽象状态(State):state接口定义了一个所有具体状态的共同方法,任何具体状态都实现这个接口,这样状态之间可以相互替换。
具体状态(ConcreteState):具体状态封装了对应的行为,用来处理来自context的请求。所以当context状态改变时对应的行为也跟着改变。
3. 优点
(1)将对象的状态和行为进行单独封装,有利于状态之间的相互替换和状态的增加。
(2)可以减少代码中多重if/else语句的使用。
(3)封装了转换规则,对客户而言就像是对象自己修改了自己的类。
4. 缺点
(1)由于单独封装状态,造成类文件增多。
(2)将行为封装到对应的状态中,在context中使用的时候就需要格外小心,否则容易造成逻辑错误。
(3)对“开闭原则”支持不太好。对于那些支持状态转换的设计下,增加或删除状态对象的时候,需要修改负责状态转换的代码,不然无法完成转换。
5. 使用场景
(1)行为随状态改变而改变的场景。
(2)多重if、else判断语句出现的场景,可以考虑使用状态模式。
6. 示例代码
6.1 交通红绿灯
(1)抽象状态
/**
* 公共状态抽象接口
*/
public interface State {
public void change(TrafficLight trafficLight);
}
(2)具体交通灯状态
/**
* 红灯状态(ConcreteState)
*/
public class RedState implements State {
@Override
public void change(TrafficLight trafficLight) {
System.out.println("红灯亮了停一停!");
try {
//红灯亮两秒
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
trafficLight.setState(new GreenState());
}
}
/**
* 绿灯状态
*/
public class GreenState implements State {
@Override
public void change(TrafficLight trafficLight) {
System.out.println("绿灯亮了行一行!");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
trafficLight.setState(new YellowState());
}
}
/**
* 黄灯状态
*/
public class YellowState implements State {
@Override
public void change(TrafficLight trafficLight) {
System.out.println("黄灯亮了等一等!");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
trafficLight.setState(new RedState());
}
}
(3)交通灯
/**
* 红绿灯对象(context)
*/
public class TrafficLight {
//红绿灯的状态
private State state;
public TrafficLight(State state) {
this.state=state;
}
public void work(){
//红绿灯一直运行
while(true){
//状态改变
state.change(this);
}
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
}
(4)客户端
public static void main(String[] args) {
//开车遇到一个红色状态的红绿灯。
TrafficLight tl=new TrafficLight(new RedState());
tl.work();
}
(5)运行结果
红灯亮了停一停!
绿灯亮了行一行!
黄灯亮了等一等!
红灯亮了停一停!
绿灯亮了行一行!
黄灯亮了等一等!
.....
(间隔两秒输出一条结果)
6.2 电灯
(1)抽象状态
/**
* 公共状态抽象接口
*/
public interface State {
public void change(Light light);
}
(2)具体电灯状态
/**
* 电灯打开状态
* 开启状态下改变状态为关闭状态
*/
public class OpenState implements State {
@Override
public void change(Light light) {
//设置电灯当前状态
light.setState(new CloseState());
System.out.println("电灯关闭了");
}
}
/**
* 电灯关闭状态
* 关闭状态下改变状态为开启状态
*/
public class CloseState implements State {
@Override
public void change(Light light) {
//设置电灯当前状态
light.setState(new OpenState());
System.out.println("电灯打开了");
}
}
(3)电灯
/**
* 电灯对象
*/
public class Light {
//电灯当前状态
private State state;
public Light() {
//设置电灯当前状态为关闭
state=new CloseState();
}
/**
* 按下电灯开关
*/
public void press(){
state.change(this);
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
}
(4)客户端
public static void main(String[] args) {
Light light=new Light();
for (int i=0;i<5;i++) {
//连续按压5次开关
light.press();
}
}
(5)运行结果
电灯打开了
电灯关闭了
电灯打开了
电灯关闭了
电灯打开了
7. 策略模式和状态模式的比较
(1)状态模式有明显的状态过渡,环境会有一个状态改变的过程。策略模式选中了一种具体的策略算法过后就不会更改了。
(2)状态模式改变状态是在外部环境的影响下自动改变的,而策略模式需要客户端主动选择某种具体的策略。
(3)状态模式和策略模式有相同的类图,但是他们的意图不同。状态模式的意图是使对象内部状态改变的时候相应的行为也随之改变,而策略模式是将每个算法单独封装起来使他们能够相互替换。
参考资料http://alaric.iteye.com/blog/1938400
【四川乐山程序员联盟,欢迎大家加群相互交流学习5 7 1 8 1 4 7 4 3】