JAVA设计模式:状态模式

声明:转载请说明来源:http://www.cnblogs.com/pony1223/p/7518226.html

一、引出状态模式

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

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

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

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

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

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

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

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

二、解决办法

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

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

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

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

1.首先定义接口:

package study.designmode.statemode.state;

public interface State {
    public void insertCoin();
    public void returnCoin();
    public void turnCrank();
    public void dispense();
    public void printstate();
}

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

准备状态:

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***");

    }

}

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

准备状态:

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***");

    }

}

初始状态:

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***");

    }

}

售出状态:

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***");

    }

}

赢家状态:

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

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.测试类

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所要组合的策略对象是哪一个.

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

时间: 2024-10-15 06:16:50

JAVA设计模式:状态模式的相关文章

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

状态模式: 允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类. 要点: 1.状态模式允许一个对象基于内部状态而拥有不同的行为 2.和程序状态机(PSM)不同,状态模式用类代表状态 3.Context会将行为委托给当前状态对象 4.通过将每个状态封装进一个类,我们把以后需要做的任何改变局部化了 5.状态模式和策略模式有相同的类图,但是它们的意思不同 6.策略模式通常会用行为或算法来配置Context类 7.状态模式允许Context随着状态的改变而改变行为 8.状态改变可以由Sta

JAVA 设计模式 状态模式

用途 状态模式 (State) 当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类. 状态模式是一种行为型模式. 结构 图-状态模式结构图 State : 定义一个接口以封装与 Context 的一个特定状态相关的行为. abstract class State {    public abstract void Handle(Context context);} ConcreteState : 每一个子类实现一个与 Context 的一个状态相关的行为. class Con

Java设计模式——状态模式(State)

1979年在湖北出土的曾侯乙编钟,每一只钟都能发出不同的音. 用面向对象的语言来讲,编钟有能够动态变化的属性,也就是它所能发出的声音.编钟的这种属性叫做状态,而编钟被叫做有状态的对象.编钟所能发出的声音是由敲击的那一只钟决定,而钟的数目个每一只钟的状态是事先确定的. 很多情况下,一个对象的行为取决于一个或多个动态变化的属性,这样的属性叫做状态,这样的对象叫做有状态的对象. 状态模式的类图如下: 状态模式设计的角色有: 抽象状态角色(State):定义一个接口,用以封装环境对象的一个特定的状态所对

Java 设计模式 -- 复合模式之二

接着上文的鸭鸣例子:Java 设计模式 -- 复合模式之一 上文中,我们的鸭鸣实现了 装饰者模式  适配器模式  工厂模式的结合 现在,又需要进行改动了,上文,是可以统计一群鸭子的叫声,现在需要能够观察个别鸭子的行为 引入观察者模式: 任何想被观察的Quackable都必须实现下面的接口 public interface QuackObservable { public void registerObserver(Observer observer); public void notifyobs

设计模式 状态模式 以自动售货机为例

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/26350617 状态模式给了我眼前一亮的感觉啊,值得学习~ 先看定义:允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类.定义又开始模糊了,理一下,当对象的内部状态改变时,它的行为跟随状态的改变而改变了,看起来好像重新初始化了一个类似的. 下面使用个例子来说明状态模式的用法,现在有个自动售货机的代码需要我们来写,状态图如下: 分析一个这个状态图: a.包含4个状态(

设计模式 - 状态模式(state pattern) 详解

状态模式(state pattern) 详解 本文地址: http://blog.csdn.net/caroline_wendy 状态模式(state pattern): 允许对象在内部状态改变时改变它的行为, 对象看起来好像修改了它的类. 建立Context类, 包含多个具体状态(concrete state)类的组合, 根据状态的不同调用具体的方法, state.handle(), 包含set\get方法改变状态. 状态接口(state interface), 包含抽象方法handle(),

一起学java设计模式--代理模式(结构型模式)

代理模式 应用软件所提供的桌面快捷方式是快速启动应用程序的代理,桌面快捷方式一般使用一张小图片来表示(Picture),通过调用快捷方式的run()方法将调用应用软件(Application)的run()方法.使用代理模式模拟该过程,绘制类图并编程实现. package ProxyPattern; interface Software { void run(); } class Application implements Software { public void run() { Syste

设计模式 - 状态模式(state pattern) 未使用状态模式 详解

状态模式(state pattern) 未使用状态模式 详解 本文地址: http://blog.csdn.net/caroline_wendy 状态模式可以控制状态的转换, 未使用设计模式时, 程序会非常繁杂. 具体方法: 1. 状态转换类. /** * @time 2014年7月11日 */ package state; /** * @author C.L.Wang * */ public class GumballMachine { final static int SOLD_OUT =

Java设计模式-代理模式之动态代理(附源码分析)

Java设计模式-代理模式之动态代理(附源码分析) 动态代理概念及类图 上一篇中介绍了静态代理,动态代理跟静态代理一个最大的区别就是:动态代理是在运行时刻动态的创建出代理类及其对象.上篇中的静态代理是在编译的时候就确定了代理类具体类型,如果有多个类需要代理,那么就得创建多个.还有一点,如果Subject中新增了一个方法,那么对应的实现接口的类中也要相应的实习该方法,不符合设计模式原则. 动态代理的做法:在运行时刻,可以动态创建出一个实现了多个接口的代理类.每个代理类的对象都会关联一个表示内部处理

Java设计模式-代理模式之动态代理(附源代码分析)

Java设计模式-代理模式之动态代理(附源代码分析) 动态代理概念及类图 上一篇中介绍了静态代理,动态代理跟静态代理一个最大的差别就是:动态代理是在执行时刻动态的创建出代理类及其对象. 上篇中的静态代理是在编译的时候就确定了代理类详细类型.假设有多个类须要代理.那么就得创建多个. 另一点,假设Subject中新增了一个方法,那么相应的实现接口的类中也要相应的实现这些方法. 动态代理的做法:在执行时刻.能够动态创建出一个实现了多个接口的代理类.每一个代理类的对象都会关联一个表示内部处理逻辑的Inv