原文出处: 屌丝码农

一、引出状态模式

假设我们现在有一个糖果机项目,那么我们知道正常一般糖果机提供给用户的行为有这么几种:投入硬币、转动曲柄、退出硬币几种行为;那么糖果机呢一般有这几中状态,待机状态、持有硬币的准备状态、运行状态即正在售出状态和初始状态 这么几种正常状态。 我们发现处于不同状态的时候,持有的行为是不一样的,图如下:

如果我们采用传统的方法来写代码,那么在投入硬币这个行为操作的时候,我们会进行状态的判断,只有在处于待机状态情况下这种行为是正常的,而其他则非正常,那么其他行为也一样,都需要去先判断下当前的状态来进行操作。得到的代码则为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
package study.designmode.statemode;
public class CandyMachine {
    final static int SoldOutState = 0; //初始状态
    final static int OnReadyState = 1 //待机状态
    final static int HasCoin = 2 //准备状态
    final static int SoldState = 3 //售出状态
    private int state = SoldOutState; //变量,用于存放当前的状态值
    private int count = 0; //糖果的数目
    public CandyMachine(int count) {
        this.count = count;
        if (count > 0) {
            state = OnReadyState;
        }
    }
    //投入硬币行为的时候,通过判断当前的状态来匹配所有的状态.
    public void insertCoin() {
        switch (state) {
        case SoldOutState:
            System.out.println("you can't insert coin,the machine sold out!");
            break;
        case OnReadyState: //只有在待机状态的时候,投入硬币行为正确,并将状态改变为准备状态
            state = HasCoin;
            System.out
                    .println("you have inserted a coin,next,please turn crank!");
            break;
        case HasCoin:
            System.out.println("you can't insert another coin!");
            break;
        case SoldState:
            System.out.println("please wait!we are giving you a candy!");
            break;
        }
    }
    //回退硬币
    public void returnCoin() {
        switch (state) {
        case SoldOutState:
            System.out
                    .println("you can't return,you haven't inserted a coin yet!");
            break;
        case OnReadyState:
            System.out.println("you haven't inserted a coin yet!");
            break;
        case HasCoin:
            System.out.println("coin return!");
            state = OnReadyState;
            break;
        case SoldState:
            System.out.println("sorry,you already have turned the crank!");
            break;
        }
    }
    //转动曲柄
    public void turnCrank() {
        switch (state) {
        case SoldOutState:
            System.out.println("you turned,but there are no candies!");
            break;
        case OnReadyState:
            System.out.println("you turned,but you haven't inserted a coin!");
            break;
        case HasCoin:
            System.out.println("crank turn...!");
            state = SoldState;
            dispense();
            break;
        case SoldState:
            System.out
                    .println("we are giving you a candy,turning another get nothing,!");
            break;
        }
    }
    //触发发放糖果行为
    private void dispense() {
        count = count - 1;
        System.out.println("a candy rolling out!");
        if (count > 0) {
            state = OnReadyState;
        } else {
            System.out.println("Oo,out of candies");
            state = SoldOutState;
        }
    }
    public void printstate() {
        switch (state) {
        case SoldOutState:
            System.out.println("***SoldOutState***");
            break;
        case OnReadyState:
            System.out.println("***OnReadyState***");
            break;
        case HasCoin:
            System.out.println("***HasCoin***");
            break;
        case SoldState:
            System.out.println("***SoldState***");
            break;
        }
    }
}

那么上面这种方式存在什么问题呢?首先很直观的感受就是:

1.存在大量的switch case 语句 当然可以用if else 也是一样的。

2.可扩展性差,并且一旦要加入一种新的状态,那么就会要修改所有的switch case 不符合开闭原则

3.没有采用面向对象的方式去封装

比如,这个时候,新增加了一种状态,赢家状态,即可以获取到两粒糖果;那么如果用上面的方式,肯定是不符合开闭原则的,同时扩展性也是不好的;那么我们有什么其它的方式来解决呢?

二、解决办法

为了解决上面的问题,我们首先分析项目中变化的部分和不变的部分,抽化出变化的部分,我们发现糖果机提供的行为一般是不变的,就是投入硬币、转动曲柄给、退回硬币、机器发放糖果;而糖果机的状态是可以变化的,可以新增出一种状态来,比如我们说的赢家状态。那么我们这个抽出变化的部分,即我们说的状态,于是出现了下面的结构设计方案:

这个结构图告诉我们,提炼出状态接口出来,然后将各个状态抽出,并去实现接口,每个状态都持有投入硬币,退回硬币,转动曲柄、售出糖果这几种行为对应的方法做出相应;而糖果机持有所有的状态,并通过引用状态接口来操作各个状态;这种设计架构就是我们说的状态模式。

状态模式定义:对象行为的变化是由于状态的变化引入,那么即当内部状态发生变化的时候,就会改变对象的行为,而这种改变视乎就改变了整个类。

那么现在采用状态模式来解决问题:

1.首先定义接口:

1
2
3
4
5
6
7
8
9
package study.designmode.statemode.state;
public interface State {
    public void insertCoin();
    public void returnCoin();
    public void turnCrank();
    public void dispense();
    public void printstate();
}

2.定义各个状态的实现类

准备状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package study.designmode.statemode.state;
import java.util.Random;
public class HasCoin implements State {
    private CandyMachine mCandyMachine;
    public HasCoin(CandyMachine mCandyMachine) {
        this.mCandyMachine = mCandyMachine;
    }
    @Override
    public void insertCoin() {
        // TODO Auto-generated method stub
        System.out.println("you can't insert another coin!");
    }
    @Override
    public void returnCoin() {
        // TODO Auto-generated method stub
        System.out.println("coin return!");
        mCandyMachine.setState(mCandyMachine.mOnReadyState);
    }
    @Override
    public void turnCrank() {
        // TODO Auto-generated method stub
        System.out.println("crank turn...!");
        Random ranwinner=new Random();
        int winner=ranwinner.nextInt(10);
        if(winner==0)
        {
            mCandyMachine.setState(mCandyMachine.mWinnerState);
        }else
        {
            mCandyMachine.setState(mCandyMachine.mSoldState);
        }
         
    }
    @Override
    public void dispense() {
    }
    @Override
    public void printstate() {
        // TODO Auto-generated method stub
        System.out.println("***HasCoin***");
    }
}

说明:我们会发现里面存在一个糖果机的属性,而之所以存在这个属性,就是因为糖果机中持有所有的状态,而在准备状态下,肯定会由于某种行为发生状态改变,而要改变的状态都在糖果机中,所以持有一个糖果机属性,下面也一样,不在重复说明。

准备状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package study.designmode.statemode.state;
public class OnReadyState implements State {
    private CandyMachine mCandyMachine;
    public OnReadyState(CandyMachine mCandyMachine)
    {
        this.mCandyMachine=mCandyMachine;
    }
    @Override
    public void insertCoin() {
        // TODO Auto-generated method stub
        System.out
        .println("you have inserted a coin,next,please turn crank!");
        mCandyMachine.setState(mCandyMachine.mHasCoin);
    }
    @Override
    public void returnCoin() {
        // TODO Auto-generated method stub
        System.out.println("you haven't inserted a coin yet!");
         
    }
    @Override
    public void turnCrank() {
        // TODO Auto-generated method stub
        System.out.println("you turned,but you haven't inserted a coin!");
         
    }
    @Override
    public void dispense() {
        // TODO Auto-generated method stub
    }
    @Override
    public void printstate() {
        // TODO Auto-generated method stub
        System.out.println("***OnReadyState***");
         
    }
}

初始状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package study.designmode.statemode.state;
public class SoldOutState implements State {
    private CandyMachine mCandyMachine;
    public SoldOutState(CandyMachine mCandyMachine)
    {
        this.mCandyMachine=mCandyMachine;
    }
    @Override
    public void insertCoin() {
        // TODO Auto-generated method stub
        System.out.println("you can't insert coin,the machine sold out!");
         
    }
    @Override
    public void returnCoin() {
        // TODO Auto-generated method stub
        System.out
        .println("you can't return,you haven't inserted a coin yet!");
    }
    @Override
    public void turnCrank() {
        // TODO Auto-generated method stub
        System.out.println("you turned,but there are no candies!");
         
    }
    @Override
    public void dispense() {
        // TODO Auto-generated method stub
    }
    @Override
    public void printstate() {
        // TODO Auto-generated method stub
        System.out.println("***SoldOutState***");
     
    }
}

售出状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package study.designmode.statemode.state;
public class SoldState implements State {
    private CandyMachine mCandyMachine;
    public SoldState(CandyMachine mCandyMachine)
    {
        this.mCandyMachine=mCandyMachine;
    }
    @Override
    public void insertCoin() {
        // TODO Auto-generated method stub
        System.out.println("please wait!we are giving you a candy!");
    }
    @Override
    public void returnCoin() {
        // TODO Auto-generated method stub
        System.out.println("you haven't inserted a coin yet!");
         
    }
    @Override
    public void turnCrank() {
        // TODO Auto-generated method stub
        System.out
        .println("we are giving you a candy,turning another get nothing,!");
    }
    @Override
    public void dispense() {
        // TODO Auto-generated method stub
         
        mCandyMachine.releaseCandy();
        if (mCandyMachine.getCount() > 0) {
            mCandyMachine.setState(mCandyMachine.mOnReadyState);
        } else {
            System.out.println("Oo,out of candies");
            mCandyMachine.setState(mCandyMachine.mSoldOutState);
        }
     
     
    }
    @Override
    public void printstate() {
        // TODO Auto-generated method stub
        System.out.println("***SoldState***");
         
    }
}

赢家状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package study.designmode.statemode.state;
public class WinnerState implements State {
    private CandyMachine mCandyMachine;
    public WinnerState(CandyMachine mCandyMachine) {
        this.mCandyMachine = mCandyMachine;
    }
    @Override
    public void insertCoin() {
        // TODO Auto-generated method stub
        System.out.println("please wait!we are giving you a candy!");
    }
    @Override
    public void returnCoin() {
        // TODO Auto-generated method stub
        System.out.println("you haven't inserted a coin yet!");
    }
    @Override
    public void turnCrank() {
        // TODO Auto-generated method stub
        System.out
                .println("we are giving you a candy,turning another get nothing,!");
    }
    @Override
    public void dispense() {
        // TODO Auto-generated method stub
         
        mCandyMachine.releaseCandy();
        if (mCandyMachine.getCount() == 0) {
            mCandyMachine.setState(mCandyMachine.mSoldOutState);
        } else {
            System.out.println("you are a winner!you get another candy!");
            mCandyMachine.releaseCandy();
            if (mCandyMachine.getCount() > 0) {
                mCandyMachine.setState(mCandyMachine.mOnReadyState);
            } else {
                System.out.println("Oo,out of candies");
                mCandyMachine.setState(mCandyMachine.mSoldOutState);
            }
        }
    }
    @Override
    public void printstate() {
        // TODO Auto-generated method stub
        System.out.println("***WinnerState***");
    }
}

3.糖果机,糖果机要持有所有的状态,并在初始化的时候,要设置其开始的状态,然后糖果的各个行为,就委托到了各个状态中自己维护,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package study.designmode.statemode.state;
public class CandyMachine {
    State mSoldOutState;
    State mOnReadyState;
    State mHasCoin;
    State mSoldState;
    State mWinnerState;
    private State state;
    private int count = 0;
    public CandyMachine(int count) {
        this.count = count;
        mSoldOutState = new SoldOutState(this);
        mOnReadyState = new OnReadyState(this);
        mHasCoin = new HasCoin(this);
        mSoldState = new SoldState(this);
        mWinnerState = new WinnerState(this);
        if (count > 0) {
            state = mOnReadyState;
        } else {
            state = mSoldOutState;
        }
    }
    public void setState(State state) {
        this.state = state;
    }
    public void insertCoin() {
        state.insertCoin();
    }
    public void returnCoin() {
        state.returnCoin();
    }
    public void turnCrank() {
        state.turnCrank();
        state.dispense();
    }
    void releaseCandy() {
        // TODO Auto-generated method stub
        if (count > 0) {
            count = count - 1;
            System.out.println("a candy rolling out!");
        }
    }
    public int getCount() {
        return count;
    }
    public void printstate() {
        state.printstate();
    }
}

4.测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package study.designmode.statemode.state;
public class MainTest {
    public static void main(String[] args) {
        CandyMachine mCandyMachine = new CandyMachine(6);
        mCandyMachine.printstate();
        mCandyMachine.insertCoin();
        mCandyMachine.printstate();
        mCandyMachine.turnCrank();
        mCandyMachine.printstate();
        mCandyMachine.insertCoin();
        mCandyMachine.printstate();
        mCandyMachine.turnCrank();
        mCandyMachine.printstate();
    }
}

结果如下:

可以和开始的传统方案对比,结果是一样的,但是具备了可扩展性。

三、总结

通过上面的例子,我们已经对状态模式有所了解,下面我们做一个总结,来回顾我们的状态模式:

1.状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
理解:这个模式将状态封装成独立的类,并将动作委托到代表当前状态的对象,这就是说行为会随着内部状态而改变。
“看起来好像修改了它的类”是什么意思呢?从客户的视角来看:如果说你使用的对象能够完全改变它的行为,那么你会觉得,这个对象实际上是从别的类实例化而来的。然而,实际上,你知道我们是在使用组合通过简单引用不同的状态对象来造成类改变的假象

2.状态模式要点

(1)客户不会和状态进行交互,全盘了解状态是 context的工作
(2)在状态模式中,每个状态通过持有Context的引用,来实现状态转移
(3)使用状态模式总是会增加设计中类的数目,这是为了要获得程序可扩展性,弹性的代价,如果你的代码不是一次性的,后期可能会不断加入不同的状态,那么状态模式的设计是绝对值得的。【同时也是一个缺点】
(4)状态类可以被多个context实例共享

3.状态模式和策略模式对比

首先让我们来看看它们之间更多的相似之处:
添加新的状态或策略都很容易,而且不需要修改使用它们的Context对象。
它们都让你的代码符合OCP原则(软件对扩展应该是开发的,对修改应该是关闭的)。在状态模式和策略模式中,Context对象对修改是关闭的,添加新的状态或策略,都不需要修改Context。
正如状态模式中的Context会有初始状态一样,策略模式同样有默认策略。
状态模式以不同的状态封装不同的行为,而策略模式以不同的策略封装不同的行为。
它们都依赖子类去实现相关行为

两个模式的差别在于它们的”意图“不同:

状态模式帮助对象管理状态,我们将一群行为封装早状态对象中,context的行为随时可委托到那些状态中的一个.随着时间的流逝,当前状态在状态对象集合中游走改变,以反映context内部状态,因此,context的行为也会跟着改变。当要添加新的状态时,不需要修改原来代码添加新的状态类即可。 而策略模式允许Client选择不同的行为。通过封装一组相关算法,为Client提供运行时的灵活性。Client可以在运行时,选择任一算法,而不改变使用算法的Context。一些流行的策略模式的例子是写那些使用算法的代码,例如加密算法、压缩算法、排序算法。客户通常主动指定context所要组合的策略对象是哪一个。

一句话:最根本的差异在于策略模式是在求解同一个问题的多种解法,这些不同解法之间毫无关联;状态模式则不同,状态模式要求各个状态之间有所关联,以便实现状态转移。

from: http://www.importnew.com/26627.html

JAVA 设计模式 : 状态模式相关推荐

  1. Java 设计模式——状态模式

    概述 很多人在说状态模式的时候总拿策略模式来进行对比,可能他们的类图会有一点类似,可我却不认为他们有多么相像.你可以阅读<Java设计模式--策略模式>这篇博客,并与本文对比,以找到蛛丝马 ...

  2. Java设计模式——状态模式【State Pattern】

    一.引言 23种设计模式大概分为三大类: 5种(创建型模式):工厂方法模式.抽象工厂模式.单例模式.原型模式.建造者模式. 7种(结构型模式):适配器模式.装饰器模式.代理模式.外观模式.桥接模式.组 ...

  3. java设计模式---状态模式

    在阎宏博士的<JAVA与模式>一书中开头是这样描述状态(State)模式的: 状态模式,又称状态对象模式(Pattern of Objects for States),状态模式是对象的行为 ...

  4. Java设计模式—状态模式

    (尊重劳动成果,转载请注明出处:https://blog.csdn.net/qq_25827845/article/details/52468077冷血之心的博客) 目录 定义如下: 个人理解: 通用 ...

  5. Java设计模式-状态模式(State)

    核心思想就是:当对象的状态改变时,同时改变其行为,很好理解!就拿QQ来说,有几种状态,在线.隐身.忙碌等,每个状态对应不同的操作,而且你的好友也能看到你的状态,所以,状态模式就两点:1.可以通过改变状 ...

  6. 深入理解Java设计模式——状态模式

    目录 一.什么是状态模式 二.状态模式应用场景 三.状态模式实现 1. 需要重构的代码 四.状态模式与策略模式区别 五.状态模式实现 一.什么是状态模式 状态模式允许一个对象在其内部状态改变的时候改变 ...

  7. java设计模式-状态模式

    状态模式介绍: 当一个对象在内在状态改变时,允许改变起行为,这个对象看起来像是改变了其类. 状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂的情况.把状态的判断逻辑转移到表示不同状态的一 ...

  8. Java设计模式(备忘录模式-解释器模式-状态模式-策略模式-职责链模式)

    Java设计模式Ⅶ 1.备忘录模式 1.1 备忘录模式概述 1.2 代码理解 2.解释器模式 2.1 解释器模式概述 3.状态模式 3.1 状态模式概述 3.2 代码理解 4.策略模式 4.1 策略模 ...

  9. Java 有限状态机 (设计模式——状态模式)

    Java 有限状态机 (设计模式--状态模式) 编写代码的时候,有时会遇见较为复杂的swith...case...和if...else...语句.这一刻有时会想到状态机,用有限状态机替换swith.. ...

最新文章

  1. 技术17期:近几年崛起的Pytorch究竟是何方神圣?
  2. 正点原子stm32f429 pcb_正点原子【STM32-F407探索者】第十六章 电容触摸按键实验
  3. 用计算机对话的小品,爆笑小品剧本台词《作弊记》
  4. xen虚拟化实战系列(二)之xen虚拟机安装
  5. L1-016. 查验身份证-PAT团体程序设计天梯赛GPLT
  6. C#对象赋值出现的诡异问题,或许你也遇到过,有待你的解决
  7. 中文文档保存到mysql_mysql 中文 存储
  8. java导出word特殊符号,Word2019中输入特殊符号的方法
  9. PowerBI 开发 第22篇:发现异常(Find Anomalies)
  10. 企业实战之部署Solarwinds Network八部众
  11. TP、FN、TN、TP详解
  12. Android 面部识别之二(调用开源面部识别算法seetaface检测)
  13. Matlab中关于数值积分及符号积分的讨论
  14. 将正常表格数据转换为横版数据(js实现)
  15. 程序猿思维释放:打破常态
  16. ios piv6遭拒绝
  17. qzezoj 1590 买玩具
  18. html调用手机NFC,如何使用手机上NFC的功能
  19. Code Clinic: R 代码诊所:R语言 Lynda课程中文字幕
  20. 手把手教你如何把小程序装进自己的APP

热门文章

  1. spring security源码分析之core包
  2. A beginner’s guide to Cache synchronization strategies--转载
  3. java 开源缓存框架--转载
  4. mvn命令安装jar包--转
  5. 【待继续研究】解析机器学习技术在反欺诈领域的应用
  6. tf-idf:信息检索
  7. 首份《顶级数据团队建设全景报告》重磅发布: 逾半数据团队称人才储备不足
  8. aws iot 连接时间_AWS IoT Core 定价
  9. MySQL-主从架构的搭建
  10. 学习笔记Hadoop(五)—— Hadoop集群的安装与部署(2)—— Hadoop集群部署模式、配置固定IP