原文:http://blog.csdn.net/hackerain/article/details/7539251
定义:
当一个对象内在的状态改变时,允许其改变行为,这个对象看起来像是改变了其类。
状态模式不太好理解,我现在还没有完全理解,这个模式给我的第一印象就是比较乱,给我的感觉好像是两个类互相依赖,①调用②的方法,②调用①的方法,弄的好乱,所以这个状态模式就不用通用类图去描述了,那样会不知所云,还是举一个实际的例子来理解吧。
这个例子是书本上的一个例子,是针对足球队比赛时,根据比分的领先还是落后,来调整其阵型,从而进入相应的状态,其示意图如下:
其对应的类图如下:
图2 足球对的状态模式类图
解释:可以注意到FootballTeam这个类维护了一个ScoreState对象,表示FootballTeam这个类维护当前的一个状态,通过其中的changeState()方法来切换维护的状态,而改变阵型的方法changeLineUp()这个方法是足球队的行为,它只是调用当前维护状态中的changeLineUp()方法,因为具体的实现是在状态类中取实现的。而在状态类中,也有一个changeState()方法,这个行为的作用是调用FootballTeam中的changeState()方法,来改变FootballTeam所维护的状态。
所以要理解这个交叉的类图,关键要抓住两点:
1、changeLineUp()方法在状态类中实现,而在FootballTeam类中调用;
2、changeState()方法在FootballTeam类中实现,而在状态类中调用;
状态模式有什么好处呢?为什么要这样做呢?
状态模式的本质是将条件语句的各个分支封装起来,从而实现了状态逻辑与动作的分离。当分支很多时,这种模式会给代码的维护带来很大的便利。比如说要增加一个状态,采用分支的话,就要涉及到多个分支的更改。而用状态模式,只需要增加一个新的状态实现类,在这个类中定义这个状态可以切换到哪些状态,这个状态下可以有什么行为,就可以了。当要修改一个状态,只需要修改这个状态类就可以了。
状态模式的封装性非常好,状态变化放置到类的内部来实现,外部的调用不用知道类内部如何实现状态和行为的切换。
上面例子的源代码如下:
- public abstract class ScoreState {
- public abstract void changeLineUp(FootballTeam footballTeam, String score);
- //这个方法是所有的子类都共有的方法,所以提取到了父类中进行实现
- protected void changeState(FootballTeam footballTeam, ScoreState state){
- footballTeam.changeState(state);
- }
- }
- /*
- * 平局状态,有两种可能切换的状态:
- * 1、对手先进球,比分落后,这时调整阵型,状态进入比分落后状态
- * 2、己方先进球,比分领先,这时调整阵型,状态进入比分领先状态
- */
- public class DrawState extends ScoreState {
- @Override
- public void changeLineUp(FootballTeam footballTeam, String score) {
- //比分落后
- if(score=="lag"){
- ScoreState lagState=new LagState();
- super.changeState(footballTeam,lagState);
- System.out.println("比分落后,阵型调整为3-4-3");
- }
- else if(score=="ahead"){
- ScoreState aheadState=new AheadState();
- super.changeState(footballTeam, aheadState);
- System.out.println("比分领先,阵型调整为5-4-1");
- }
- }
- }
- /*
- * 比分落后,只有一种可能切换的状态:己方进球扳平比分,比分持平,调整阵型,状态进入持平状态
- */
- public class LagState extends ScoreState {
- @Override
- public void changeLineUp(FootballTeam footballTeam, String score) {
- if(score=="draw"){
- ScoreState drawState=new DrawState();
- super.changeState(footballTeam, drawState);
- System.out.println("比分持平,阵型调整为4-4-2");
- }
- }
- }
- /*
- * 比分领先,只有一种可能切换的状态:对手进球扳平比分,比分持平,调整阵型,状态进入持平状态
- */
- public class AheadState extends ScoreState {
- @Override
- public void changeLineUp(FootballTeam footballTeam, String score) {
- if(score=="draw"){
- ScoreState drawState=new DrawState();
- super.changeState(footballTeam, drawState);
- System.out.println("比分持平,阵型调整为4-4-2");
- }
- }
- }
- public class Client {
- public static void main(String[] args) {
- String score=null;
- //定义一个球队,初始状态为持平状态
- ScoreState drawState=new DrawState();
- FootballTeam footballTeam=new FootballTeam(drawState);
- //比分落后
- score="lag";
- footballTeam.changeLineUp(score);
- //扳平比分
- score="draw";
- footballTeam.changeLineUp(score);
- //比分领先
- score="ahead";
- footballTeam.changeLineUp(score);
- }
- }
状态模式的优点:
1、结构清晰
避免许多switch 或者if else语句的使用,避免程序的复杂性,提高系统的可维护性
2、遵循设计原则
很好体现开闭原则和单一职责原则,每个状态一个子类,要增加状态就要增加子类,要修改状态,只要修改一个子类就可以
3、封装性非常好
状态变换放置到类的内部实现,外部的调用不用知道类内部如何实现状态和行为的变换
状态模式的使用场景
1、行为随状态改变而改变的场景
这也是状态模式的根本出发点,如权限设计,人员的状态不同即使执行相同的行为结果也会不同,在这种情况下需要考虑使用状态模式
2、条件、分支判断语句的替代者
程序中大量使用switch语句或if判断语句会导致程序结构不清晰,逻辑混乱,使用状态模式可以很好的避免这个问题,它通过扩展子类实现条件的判断处理