设计模式系列之状态模式【十一】

1. 前言

《Head First设计模式》中关于状态模式的例子举得很好,如下:

假设我们现在有一个糖果机项目,那么我们知道一般糖果机提供给用户的行为有这么几种:投入硬币、转动曲柄、退出硬币几种行为;糖果机一般有这几种状态:待机状态、持有硬币的准备状态、运行状态即正在售出状态和初始状态。 我们发现处于不同状态的时候,持有的行为是不一样的:
设计模式系列之状态模式【十一】_第1张图片
如果采用传统的方法来写代码,那么在投入硬币这个行为操作的时候,会进行状态的判断,只有处于待机状态下是正常的,而其他则非正常,那么其他行为也一样,都需要去先判断下当前的状态来进行操作。得到的代码则为:

package com.linyf.demo.pattern.state;

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 语句
  2. 可扩展性差,并且一旦要加入一种新的状态,那么就会要修改所有的switch case,不符合开闭原则
  3. 没有采用面向对象的方式去封装

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

2. 定义

为了解决上面的问题,首先分析项目中变化的部分和不变的部分,抽出变化的部分,我们发现糖果机提供的行为一般是不变的,就是投入硬币、转动曲柄、退回硬币、发放糖果;而糖果机的状态是可以变化的,可以新增出一种状态来,比如赢家状态。于是出现了下面的结构设计方案:
设计模式系列之状态模式【十一】_第2张图片
从结构图可以看出,提炼出状态接口,然后将各个状态抽出,并去实现接口,每个状态都持有投入硬币,退回硬币,转动曲柄、售出糖果这几种行为对应的方法做出相应;而糖果机持有所有的状态,并通过引用状态接口来操作各个状态;这种设计架构就是状态模式。

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

3. 重构

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

3.1 定义接口

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

3.2 定义各个状态的实现类

3.2.1 待机状态

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

    }
}

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

3.2.2 准备状态

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

}

3.2.3 初始状态

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

}

3.2.4 售出状态

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

}

3.2.5 赢家状态

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.3 糖果机

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

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();
    }
}

3.4 测试

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();
    }
}

你可能感兴趣的:(java,java,设计模式)