定义
状态模式( State Pattern ) :允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。其别名为状态对象( Objects for States ),状态模式是一种对象行为型模式。
模式中的角色
- Context: 环境类
- State: 抽象状态角色
- ConcreteState: 具体状态角色类
角色UML关系图如下:
状态模式和策略模式的区别
- 状态模式 : 状态是系统自身的固有的,调用者不能控制系统的状态转移。比如,一个请假单有“部长审批”-“经理审批”-“审批通过”-“审批不通过”等状态,请假者没有办法将一个部长都还没审批完的请假单提交给经理,这个状态转换只能系统自己完成。
- 策略模式 : 策略是外界给的,策略怎么变,是调用者考虑的事情,系统只是根据所给的策略做事情。这时系统很像是一台电脑,根据指令执行动作,打一鞭子滚一滚。
使用场景:
对象的状态决定对象的行为,在运行时根据状态动态调整对象的行为。
代码中有复杂的if else判断,且这些分支判断和状态有关系。
实际场景:
(1)电视开关机状态,电视开机状态下才可以进行各种操作,关机状态下,不能进行各项操作。
(2)WIFI状态,WIFI开状态下可以连接WIFI,关闭状态不可进行操作。
(3)登录状态,这个在开发中较为常用,一般在进入系统实现分享转发等操作时要先判断用户的登录状态,若已经登录则可进行操作,否则提示用户登录。
模拟电脑的开机,关机,死机三个状态
UML图:
java代码:
抽象状态接口
package demo13;
/**
*
* @ClassName: ComputerState
* @Description:电脑抽象状态
* @author cheng
* @date 2017-8-17 下午03:01:56
*/
public interface ComputerState {
/**
*
* @Title: playGame
* @Description:玩游戏
*/
void playGame();
/**
*
* @Title: watchVideo
* @Description:看视频
*/
void watchVideo();
/**
*
* @Title: writingCode
* @Description:写代码
*/
void writingCode();
/**
*
* @Title: listenMusic
* @Description:听音乐
*/
void listenMusic();
}
具体状态类
package demo13;
/**
*
* @ClassName: ComputerOnState
* @Description: 电脑开机状态
* @author cheng
* @date 2017-8-17 下午03:10:25
*/
public class ComputerOnState implements ComputerState {
@Override
public void listenMusic() {
System.out.println("电脑开机状态,听音乐中....");
}
@Override
public void playGame() {
System.out.println("电脑开机状态,玩游戏中....");
}
@Override
public void watchVideo() {
System.out.println("电脑开机状态,看视频中....");
}
@Override
public void writingCode() {
System.out.println("电脑开机状态,写代码中....");
}
}
package demo13;
/**
*
* @ClassName: ComputerOffState
* @Description:电脑关机状态
* @author cheng
* @date 2017-8-17 下午03:12:56
*/
public class ComputerOffState implements ComputerState {
@Override
public void listenMusic() {
System.out.println("电脑关机状态,请先开机....");
}
@Override
public void playGame() {
System.out.println("电脑关机状态,请先开机....");
}
@Override
public void watchVideo() {
System.out.println("电脑关机状态,请先开机....");
}
@Override
public void writingCode() {
System.out.println("电脑关机状态,请先开机....");
}
}
package demo13;
/**
*
* @ClassName: ComputerErrorState
* @Description:电脑死机状态
* @author cheng
* @date 2017-8-17 下午03:15:44
*/
public class ComputerErrorState implements ComputerState {
@Override
public void listenMusic() {
System.out.println("电脑死机状态,请先维修....");
}
@Override
public void playGame() {
System.out.println("电脑死机状态,请先维修....");
}
@Override
public void watchVideo() {
System.out.println("电脑死机状态,请先维修....");
}
@Override
public void writingCode() {
System.out.println("电脑死机状态,请先维修....");
}
}
上下文
package demo13;
/**
*
* @ClassName: ComputerStateContext
* @Description:电脑状态上下文
* @author cheng
* @date 2017-8-17 下午03:17:18
*/
public class ComputerStateContext {
// 持有电脑抽象状态的引用
private ComputerState state;
/**
*
* @Title: changeState
* @Description: 改变状态
* @param state
*/
public void changeState(ComputerState state) {
this.state = state;
}
/**
*
* @Title: onComputer
* @Description:开机
*/
public void onComputer() {
System.out.println("==============电脑开机=================");
changeState(new ComputerOnState());
}
/**
*
* @Title: offComputer
* @Description:关机
*/
public void offComputer() {
System.out.println("==============电脑关机=================");
changeState(new ComputerOffState());
}
/**
*
* @Title: errorComputer
* @Description:死机
*/
public void errorComputer() {
System.out.println("==============电脑死机=================");
changeState(new ComputerErrorState());
}
/**
*
* @Title: listenMusic
* @Description:调用该状态下的方法
*/
public void listenMusic() {
state.listenMusic();
}
public void playGame() {
state.playGame();
}
public void watchVideo() {
state.watchVideo();
}
public void writingCode() {
state.writingCode();
}
}
测试
package demo13;
/**
*
* @ClassName: ClientTest
* @Description:测试
* @author cheng
* @date 2017-8-17 下午03:25:59
*/
public class ClientTest {
public static void main(String[] args) {
ComputerStateContext context = new ComputerStateContext();
//开机状态
context.onComputer();
context.playGame();
context.listenMusic();
context.watchVideo();
context.writingCode();
//关机状态
context.offComputer();
context.playGame();
context.listenMusic();
context.watchVideo();
context.writingCode();
//死机状态
context.errorComputer();
context.playGame();
context.listenMusic();
context.watchVideo();
context.writingCode();
}
}
运行结果
模式分析
状态模式描述了对象状态的变化以及对象如何在每一种状态下表现出不同的行为。
状态模式的关键是引入了一个抽象接口来专门表示对象的状态,这个类我们叫做抽象状态接口,而对象的每一种具体状态类都实现了该类,并在不同具体状态类中实现了不同状态的行为,包括各种状态之间的转换。
在状态模式结构中需要理解环境类与抽象状态类的作用:
环境类实际上就是拥有状态的对象,环境类有时候可以充当状态管理器(State Manager)的角色,可以在环境类中对状态进行切换操作。
抽象状态类可以是抽象类,也可以是接口,不同状态类就是继承这个父类的不同子类,状态类的产生是由于环境类存在多个状态,同时还满足两个条件: 这些状态经常需要切换,在不同的状态下对象的行为不同。因此可以将不同对象下的行为单独提取出来封装在具体的状态类中,使得环境类对象在其内部状态改变时可以改变它的行为,对象看起来似乎修改了它的类,而实际上是由于切换到不同的具体状态类实现的。由于环境类可以设置为任一具体状态类,因此它针对抽象状态类进行编程,在程序运行时可以将任一具体状态类的对象设置到环境类中,从而使得环境类可以改变内部状态,并且改变行为。
优点
封装了转换规则。
枚举可能的状态,在枚举状态之前需要确定状态种类。
将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
缺点
状态模式的使用必然会增加系统类和对象的个数。
状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
状态模式对“开闭原则”的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态;而且修改某个状态类的行为也需修改对应类的源代码。