状态模式

使用场景

业务中比较多的状态,不同状态下需要做的事情不同。这样,每个步骤中都需要判断一下当前属于什么状态,状态过滤完成以后对相应的状态作出处理。当前状态执行完成以后,可能需要根据条件进入下一个状态(可能是下一个状态,可能是上一个状态,可能是终止或者回到初始状态等)。这种逻辑判断完以后一般都会出现比较多的if...else或者switch...case.最可怕的是后面如果加入一个状态或者修改状态相应的处理动作,这样就比较麻烦了。状态模式通过把状态拆分成不同的状态类来拆解条件。这样,在以后改变需求时具有比较好的扩展性。

具体例子

这里举《Head First 设计模式》中的一个例子。

模拟一个糖果机销售糖果的过程。在没有投入钱币时,糖果机的状态为 没有25分钱 如果有人投入了25分钱,那么状态为 有25分钱。有25分钱的状态下可以转动曲柄,走到下一个状态 售出糖果。售出糖果以后 如果糖果数大于零,糖果机回到 没有25分钱 状态。 否则,如果糖果数等于零,那么到达 糖果售罄 状态。此时如果在投入25分钱,那么将执行退回25分钱操作。具体状态转化如下图所示:

由图可知:

糖果机的状态包括:没有25分钱,有25分钱,售出糖果,糖果售罄 四种状态。糖果机的动作包括:投入25分钱,退回25分钱,转动曲柄,发放糖果 等动作

对于每一步操作,都需要判断状态,然后做出动作,接着转换成相应的状态。

把上述状态和动作转换为代码,如下所示:

 1 public class CandyMachine{
 2     private final int SOLD_OUT   = 0;//售罄
 3     private final int NO_QUARTZ  = 1;//没有25分钱
 4     private final int HAS_QUARTZ = 2;//有25分钱
 5     private final int SOLD       = 3;//售出
 6
 7     private int state = SOLD_OUT;//初始化状态值state为售罄状态
 8     private int count = 0;//糖果数目
 9
10     public CandyMachine(int count){
11         this.count = count;
12         if(count > 0){
13             this.state = NO_QUARTZ;
14         }
15     }
16
17     //投入25分钱
18     public void insertQuartz(){
19         if(state == HAS_QUARTZ){
20             System.out.println("已经投了钱,不能重复投");
21         }else if(state == NO_QUARTZ){
22             state = HAS_QUARTZ;
23             System.out.println("投币成功");
24         }else if(state == SOLD_OUT){
25             System.out.println("不能投币,糖果已售罄");
26         }else if(state == SOLD) {
27             System.out.println("请等待,正在发放糖果");
28         }
29     }
30
31     //退回25分钱
32     public void ejectQuartz(){
33         if(state == HAS_QUARTZ){
34             System.out.println("正在退钱");
35         }else if(state == NO_QUARTZ){
36             System.out.println("未投钱,不能退钱");
37         }else if(state == SOLD_OUT){
38             System.out.println("不能退币,糖果已售罄");
39         }else if(state == SOLD) {
40             System.out.println("已经转动曲柄,不能退钱");
41         }
42     }
43
44     //转动曲柄
45     public void turnCrank(){
46         if(state == SOLD){
47             System.out.println("重复转动不能得到两次糖果");
48         }else if(state == NO_QUARTZ){
49             System.out.println("未投币,没有糖果发放");
50         }else if(state == SOLD_OUT){
51             System.out.println("糖果售罄,没有糖果发放");
52         }else if(state == HAS_QUARTZ){
53             System.out.println("已转动曲柄");
54             state = SOLD;
55             dispense();
56         }
57     }
58
59     //发放糖果
60     public void dispense(){
61         if(state == SOLD){
62             System.out.println("糖果即将发放");
63             count--;
64             if(count == 0){
65                 System.out.println("这是最后一颗糖果,已售罄");
66                 state == SOLD_OUT;
67             }else{
68                 state = NO_QUARTZ;
69             }
70         }else if(state == NO_QUARTZ){
71             System.out.println("需要先付钱");
72         }else if(state == SOLD_OUT){
73             System.out.println("没有糖果发放");
74         }else if(state == HAS_QUARTZ){
75             System.out.println("没有糖果发放");
76         }
77     }
78 }

代码比较简单,就是每种动作对应一个函数,在每种动作中需要对状态进行逐一判断。如果符合,则需要转换为下一个状态。否则,仅简单打印不符合原由。至少到目前来看,代码是合理的而且正常运作。

糖果机在发放使用后效果不错,糖果公司为了进一步提高销售量,又提出了一个新的需求:当个赢家!当曲柄转动时,有10%的机率掉下来的是两个糖果。

该来的躲不掉,需求变动!!!

大致思考一下为了实现这个需求需要做的事情:1、需要加一个赢家状态 WINNER(easy) 2、在每个动作上面都要添加这个状态的判断(烦)3、转动曲柄和发放糖果动作逻辑要改(烦),综合一下,觉得加了一个状态实在麻烦,万一加完这个状态以后再有新需求!!!,这个就越来越复杂,,,这时候考虑用状态模式。

例子

首先定义一个状态接口State,里面包含了要执行的动作函数。对于上述例子,具体如下:

 1 public interface State {
 2     //投入25分钱
 3     void insertQuartz();
 4     //退回25分钱
 5     void ejectQuartz();
 6     //转动曲柄
 7     void turnCrank();
 8     //发放糖果
 9     void dispense();
10 }

接着每个状态定义为一个类,并继承State接口。类图如下:

这样在每一个状态类里面分别实现这些东西,这里贴出一个类的具体实现:

 1 /**
 2  * 出售状态
 3  */
 4 public class SoldState implements State{
 5     private CandyMachine candyMachine;
 6     public SoldState(CandyMachine candyMachine) {
 7         this.candyMachine = candyMachine;
 8     }
 9     @Override
10     public void insertQuartz() {
11         System.out.println("请等待,正在发放糖果");
12     }
13     @Override
14     public void ejectQuartz() {
15         System.out.println("已经转动曲柄,不能退钱");
16     }
17     @Override
18     public void turnCrank() {
19         System.out.println("重复转动不能得到两次糖果");
20     }
21     @Override
22     public void dispense() {
23         candyMachine.releaseBall();
24         if(candyMachine.getCount() > 0){
25             candyMachine.setState(candyMachine.getNoQuartzState());
26         }else{
27             candyMachine.setState(candyMachine.getSoldOutState());
28         }
29     }
30 }

发放糖果类这样写:

 1 public class CandyMachine {
 2     private int count = 0;
 3     private State soldState;
 4     private State noQuartzState;
 5     private State hasQuartzState;
 6     private State soldOutState;
 7     private State state = soldOutState;
 8     public CandyMachine(int count) {
 9         this.count = count;
10         this.soldState = new SoldState(this);
11         this.noQuartzState = new SoldOutState(this);
12         this.hasQuartzState = new HasQuartzState(this);
13         this.soldOutState = new NoQuartzState(this);
14         if(count > 0){
15             state = noQuartzState;
16         }
17     }
18     public void insertQuartz() {
19         state.insertQuartz();
20     }
21     public void ejectQuartz() {
22         state.ejectQuartz();
23     }
24     public void turnCrank() {
25         state.turnCrank();
26         state.dispense();
27     }
28     //发放糖果
29     public void releaseBall(){
30         System.out.println("糖果即将发放");
31         if(count != 0){
32             count--;
33         }
34     }
35 }

这样的话,在状态类中每一种动作是明确的。就是对该种状态进行处理,所以可以看到,关于状态的判断的if...else就没有了。

再来看对于上面提到的需求变更的问题。当个赢家!当曲柄转动时,有10%的机率掉下来的是两个糖果。

新增一个赢家状态类。

 1 public class WinnerState implements State {
 2     private CandyMachine candyMachine;
 3     public WinnerState(CandyMachine candyMachine) {
 4         this.candyMachine = candyMachine;
 5     }
 6     @Override
 7     public void insertQuartz() {}
 8     @Override
 9     public void ejectQuartz() {}
10     @Override
11     public void turnCrank() {}
12     @Override
13     public void dispense() {
14         candyMachine.releaseBall();
15         //这里处理逻辑。。。
16     }
17 }

到这里就差不多了。回想一下,这里其实就是两种思路。第一种是从四种动作入手,在每种动作中对每种状态进行判断。第二种是从四种状态的角度去看,每种状态要处理四种动作。动作不再需要状态判断,因为在一个状态类中时状态是确定的。第二种方式多了几个状态类,但更好扩展,以后有别的状态的时候只需要新增一个状态就好了。而且状态类中的动作函数不会太复杂,以后有改动时会比较好改。

还有一点需要注意:这里的状态只是状态模式的一种应用(不限于是状态),也就是说,类似于这种判断的场景都可以使用。例如:实现计算器的符号(+-*/)也可以用这种模式。即每种符号一种状态。总的来说也就是这种需要 多个分支条件的 都可以用状态模式。

状态模式

  • 状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。

1、对象在内部对象改变时改变它的行为:拿糖果机的例子来说,就是每个状态是一个状态类,当糖果机的状态不同时(例如NO_QUAETZ、HAS_QUARTZ),那么同一个动作的行为也是不同的。

2、对象看起来好像修改了它的类:这个就是对于State接口,通过引用不同的实现类看起来是不同的类实例化来实现的一样。

状态模式的类图如下

这个Context拥有一些状态,在例子中就是CandyMachine。Context中提供对状态的操作。像setState()。

State是一个接口,定义了状态类的一些动作。ConcreteStateA和ConcreteStateB是具体的状态类。状态类中实现动作在该状态下的操作。

注意

1、在每个具体状态类中引用了一个Context,并通过构造函数实例Context,把Context实例引进来就可以操作状态转化以及状态之间共有的部分,例如例子中的糖果机中糖果数量(count)

2、状态流程运转是这样的:初始时(有糖果)处于NO_QUATRZ状态。当投入钱币时,调用insertQuartz()方法,这时候state的引用是NoQuartzState类的引用。然后在insertQuartz()成功之后状态转换为HAS_QUARTZ状态。这时候state的引用就是HasQuartzState类的引用了。当再调用enjectQuartz()方法时进入的是HasQuartzState类里面的enjectQuartz()方法。以此类推,,,

时间: 2024-10-25 18:13:16

状态模式的相关文章

大话设计模式读书笔记--12.状态模式

定义 状态模式定义: 当一个对象的内在状态改变时,允许改变其行为,这个对象看起来改变了其类 消除庞大的条件分支,将特定状态的行为放入一个对象中 生活中:开灯和关灯是两个状态 模式结构 Context: 上下文环境,维护一个状态实例,定义当前的状态 State: 抽象状态类,定义一个接口,封装与Context的一个特定状态相关的行为 ConcreteState:具体状态.实现Context的一个特定状态相关的行为 代码实现 场景: 12店之前是休闲状态, 之后是忙碌状态 点击下载代码 特点及使用场

设计模式整理_状态模式

状态模式允许对象在内部状态改变的时候,改变它的行为,对象看起来好像修改了它的类.因为这个模式将状态封装成为独立的类,并将动作委托到代表当前状态的对象,而行为会随着内部状态而改变. 在状态模式中,Context内部持有状态,State接口定义了一个所有具体状态的共同接口,任何状态都实现这个相同的接口,这样一来,各个状态可以互相替换.具体状态实现状态接口,所以当Context改变状态的时候,行为也跟着改变.而不管在什么时候,只要有人调用Context的具体方法,它就会用来委托状态处理,下面用具体事例

大量逻辑判断优化的思路——责任链模式复习总结及其和状态模式对比

俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习!涉及的总结知识点如下: 责任链模式概念和例子 使用的条件 和状态模式的比较分析 责任链的优缺点 纯的责任链和不纯的责任链 javax.servlet.Filter#doFilter()方法源码分析 基于AOP思想,模拟一个拦截器 前面说了一个状态模式,总结过程中发现和这个责任链的使用场景很类似,都是为了解耦大量复杂业务逻辑判断的,那么他们有什么不同呢?回忆状态模式——状态模式允许通过改变对象的内部状态而改变对象自身的行为,这个对象

第十六章 状态模式

好处:将与特定状态相关的行为局部化,并将不同状态的行为分割开来. 当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,就可以考虑使用状态模式. /** * Created by hero on 16-4-4. */ public abstract class State { public abstract void handle(Context context); } /** * Created by hero on 16-4-4. */ public class Con

23状态模式

 1状态模式的核心内容是: A:状态类 B:工作类 2状态模式的作用:通过状态值自动跳转到要执行的类. 3状态模式具体描述 状态模式:状态模式允许一个对象在其内部状态改变的时候改变行为. 这个对象看上去象是改变了它的类一样.状态模式把所研究的对象的行 为包装在不同的状态对象里,每一个状态对象都属于一个抽象状态类的 一个子类.状态模式的意图是让一个对象在其内部状态改变的时候, 其行为也随之改变.状态模式需要对每一个系统可能取得的状态创立一个状态类的 子类.当系统的状态变化时,系统便改变所选的子

学习日记之状态模式和Effective C++

状态模式(State):当一个对象内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类. (1),状态模式主要负责解决的是当控制一个对象转换的条件表达式过于复杂时的情况.把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简化. (2),状态模式的好处是将与特定状态相关的行为局部化,并且将不同状态的行为分割开来. (3),将特定的状态相关的行为都放入一个对象中,由于所有与状态相关的代码都存在于某个ConcreteState中,所以通过定义新的子类可以很容易地增加新的状态和

Java设计模式(十) 备忘录模式 状态模式

(十九)备忘录模式 备忘录模式目的是保存一个对象的某个状态,在适当的时候恢复这个对象. class Memento{ private String value; public Memento(String value){ this.value = value; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } } class Storage

State状态模式(C++实现)

状态模式:状态模式的意图是,允许一个对象在其内部状态改变时,改变它的行为.看起来就像是改变了它的类一样. 主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况.把状态的判断逻辑转移到表示不同的一系列类当中,可以把复杂的逻辑判断简单化. 类图如下图所示 State类,抽象状态类,定义一个接口以封装与Context的一个特定状态相关的行为.ConcreteState类,具体状态,每一个子类实现一个与Context的一个状态相关的行为.Context类,维护一个ConcreteState子类

设计模式(20)--State(状态模式)--行为型

作者QQ:1095737364    QQ群:123300273     欢迎加入! 1.模式定义: 状态模式,又称状态对象模式(Pattern of Objects for States),状态模式是对象的行为模式. 状态模式允许一个对象在其内部状态改变的时候改变其行为.这个对象看上去就像是改变了它的类一样. 2.模式特点: 状态模式的主要优点在于封装了转换规则,并枚举可能的状态,它将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为,还可以

【设计模式】 模式PK:策略模式VS状态模式

1.概述 行为类设计模式中,状态模式和策略模式是亲兄弟,两者非常相似,我们先看看两者的通用类图,把两者放在一起比较一下. 策略模式(左)和状态模式(右)的通用类图. 两个类图非常相似,都是通过Context类封装一个具体的行为,都提供了一个封装的方法,是高扩展性的设计模式.但根据两者的定义,我们发现两者的区别还是很明显的:策略模式封装的是不同的算法,算法之间没有交互,以达到算法可以自由切换的目的:而状态模式封装的是不同的状态,以达到状态切换行为随之发生改变的目的.这两种模式虽然都有变换的行为,但