状态模式

  • 定义:
    当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。
    状态模式主要解决的是当控制一个对象状态的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简化。
  • UML:


    image.png
  • 模型:
    投币的糖果机,糖果机根据内部的状态,表现出是否可以投币,是否可以退币,是否要掉落糖果等,例如,在未投币的状态下是不能退币的,在已投币未掉落糖果的情况下也不能再次投币的。而状态模式就能很好的解决这样复杂的逻辑判断,解决的方式是将这些逻辑转移到多个类中。
//首先我们抽象出所有的状态下都应该有的行为。
public interface State {
        //投硬币
    public void insertCoin();
        //返回硬币
    public void returnCoin();
        //转动把手 出糖果
    public void turnCrank();
         //掉落糖果
    public void dispense();
         //输出状态
    public void printstate();
}

//构建一个糖果机

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() {
        if (count > 0) {
            count = count - 1;
            System.out.println("a candy rolling out!");
        }
    }
    public int getCount() {
        return count;
    }
    public void printstate() {
        state.printstate();
    }
}

//糖果机可用状态

public class OnReadyState implements State {
    private CandyMachine mCandyMachine;
    public OnReadyState(CandyMachine mCandyMachine){
        this.mCandyMachine=mCandyMachine;
    }
       //当前状态只能投入硬币,不提供其他行为
    @Override
    public void insertCoin() {
        System.out
        .println("you have inserted a coin,next,please turn crank!");
               //当投入硬币后,更改糖果机的状态
        mCandyMachine.setState(mCandyMachine.mHasCoin);
    }
    @Override
    public void returnCoin() {
        System.out.println("you haven't inserted a coin yet!");
    }
    @Override
    public void turnCrank() {
        System.out.println("you turned,but you haven't inserted a coin!");
    }
    @Override
    public void dispense() {}
    @Override
    public void printstate() {
        System.out.println("***OnReadyState***");
    }
}

//有硬币的状态

public class HasCoin implements State {
    private CandyMachine mCandyMachine;
    public HasCoin(CandyMachine mCandyMachine) {
        this.mCandyMachine = mCandyMachine;
    }
    @Override
    public void insertCoin() {
        System.out.println("you can't insert another coin!");
    }
        //当前状态可用回退硬币
    @Override
    public void returnCoin() {
        System.out.println("coin return!");
    mCandyMachine.setState(mCandyMachine.mOnReadyState);
    }
        //当前状态可用转动把手掉落糖果
    @Override
    public void turnCrank() {
        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() {
        System.out.println("***HasCoin***");
    }
}

//销售状态

public class SoldState implements State {
    private CandyMachine mCandyMachine;
    public SoldState(CandyMachine mCandyMachine){
        this.mCandyMachine=mCandyMachine;
    }
    @Override
    public void insertCoin() {
        System.out.println("please wait!we are giving you a candy!");
    }
    @Override
    public void returnCoin() {
        System.out.println("you haven't inserted a coin yet!");
    }
    @Override
    public void turnCrank() {
        System.out
        .println("we are giving you a candy,turning another get nothing,!");
    }
    @Override
    public void dispense() {
                //掉落糖果
        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() {
        System.out.println("***SoldState***");
    }
}

//得到幸运糖果的状态,可以再次获得一次糖果

public class WinnerState implements State {
    private CandyMachine mCandyMachine;
    public WinnerState(CandyMachine mCandyMachine) {
        this.mCandyMachine = mCandyMachine;
    }
    @Override
    public void insertCoin() {
        System.out.println("please wait!we are giving you a candy!");
    }
    @Override
    public void returnCoin() {
        System.out.println("you haven't inserted a coin yet!");
    }
    @Override
    public void turnCrank() {
        System.out
                .println("we are giving you a candy,turning another get nothing,!");
    }
    @Override
    public void dispense() {
        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() {
        System.out.println("***WinnerState***");
    }
}

//卖光了状态

public class SoldOutState implements State {
    private CandyMachine mCandyMachine;
    public SoldOutState(CandyMachine mCandyMachine){
        this.mCandyMachine=mCandyMachine;
    }
    @Override
    public void insertCoin() {
        System.out.println("you can't insert coin,the machine sold out!");
    }
    @Override
    public void returnCoin() {
        System.out
        .println("you can't return,you haven't inserted a coin yet!");
    }
    @Override
    public void turnCrank() {
        System.out.println("you turned,but there are no candies!");
    }
    @Override
    public void dispense() {}
    @Override
    public void printstate() {
        System.out.println("***SoldOutState***");
    }
}

测试

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();
    }
}
  • 总结:
    如果是按照传统的面向对象的方式,糖果机自己维护当前的状态,那么糖果机对外提供的每个方法都会去switch每一个状态,做不同的判断处理,还要考虑当前的糖果个数,想想逻辑就很复杂,很容易出错。而状态模式,将这些复杂的判断都转移到每个状态类中,状态类最清楚那些方法该怎么处理。糖果机只需要关心自己有几种状态,提供那些方法使用。除此之外,状态模式很像策略模式,其实他们的UML是一样的,但意图却完全不一样,策略模式是让用户指定更换的策略算法,而状态模式是状态在满足一定条件下的自动更换,用户无法指定状态,最多只能设置初始状态。
    总的来说:态模式关键是各个状态子类必须知道下一个状态是啥,且要把逻辑判断转移到各个状态子类中,客户端不需要了解状态迁移的顺序,但是策略目的是针对单一算法的运行时替换,客户端需要事先了解策略,主动去选择合适的策略,不存在状态的自动迁移!
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342