资讯专栏INFORMATION COLUMN

开发之路(设计模式十一:状态模式)

Meils / 3159人阅读

摘要:重构旧代码,取而代之方式是将动作委托给状态类。注这个模式是将状态封装成为独立地类,并将动作委托给代表当前状态的对象。

通过改变对象内部状态帮助对象控制行为

以一个简单例子说明,假设我们要模拟制造一台糖果机器,对方给你的机器流程图如下

ok,我们现在简单分析这张状态图,可将状态提取出来:有硬币,无硬币,售出糖果,糖果售空四个状态,行为动作提取出来:投入一个硬币,退回一个硬币,转动曲柄,发放糖果四个行为,当然还有一些特殊情况,具体情况参考代码

代码如下(参考)

GumballMachine

public class GumballMachine {
    /**
     * 状态模式 糖果机的状态都用一个不同整数表示
     * 
     */
    final static int SOLD_OUT = 0;// 糖果售空
    final static int NO_QUARTER = 1;// 没有投25分钱
    final static int HAS_QUARTER = 2;// 投了25分钱
    final static int SOLD = 3;// 糖果售出

    // 当前状态
    int state = SOLD_OUT;
    // 用来追踪糖果数量
    int count = 0;

    public GumballMachine(int count) {
        this.count = count;
        if (count > 0) {
            // 如果有糖果,机器变为没有投25分钱状态(待购买状态)
            state = NO_QUARTER;
        }
    }

    // 投入25分钱方法
    public void insertQuarter() {
        if (state == HAS_QUARTER) {
            System.out.println("已经投过币了,不能再投了");
        } else if (state == NO_QUARTER) {
            state = HAS_QUARTER;
            System.out.println("请投入一枚硬币");
        } else if (state == SOLD_OUT) {
            System.out.println("不能再投币了,机器已经售空了");
        } else if (state == SOLD) {
            System.out.println("请稍等,正在为你出糖果");
        }
    }

    // 退出25分钱方法
    public void ejectQuarter() {
        // 1当客户要退钱是
        if (state == HAS_QUARTER) {
            System.out.println("零钱退回");
            state = NO_QUARTER;// 进入没投币状态
        } else if (state == NO_QUARTER) {
            System.out.println("你没有投入硬币");
        } else if (state == SOLD) {
            System.out.println("对不起,你已经转动了曲轴,无法退币了");
        } else if (state == SOLD_OUT) {
            System.out.println("糖果售空,无法退币");
        }
    }

    // 转动曲轴方法(顾客)
    public void turnCrank() {
        if (state == HAS_QUARTER) {
            System.out.println("请转动曲轴");
            state = SOLD;// 状态变为售出状态
            dispense();// 调用发放糖果方法
        } else if (state == SOLD) {
            System.out.println("转动两次也不给你糖果");
        } else if (state == NO_QUARTER) {
            System.out.println("请先投硬币");
        } else if (state == SOLD_OUT) {
            System.out.println("机器售空");
        }

    }

    // 发放糖果方法
    private void dispense() {
        if (state == SOLD) {
            System.out.println("一包糖果出来了");
            count -= 1;
            // 当糖果数量0时候呢?
            if (count == 0) {
                System.out.println("哎呀,糖果售空了~~");
                state = SOLD_OUT;
            } else {
                state = NO_QUARTER;
            }
        } else if (state == NO_QUARTER) {
            System.out.println("你需要投入硬币");
        } else if (state == SOLD_OUT) {
            System.out.println("没有糖果了");
        } else if (state == HAS_QUARTER) {
            System.out.println("没有糖果了");
        }
    }

    // 重写toString()方法输入糖果机信息
    @Override
    public String toString() {
        StringBuffer result = new StringBuffer();
        result.append("欢迎使用糖果机
");
        result.append("糖果数量:" + count + "
");
        result.append("当前糖果机状态:" + state);
        return result.toString();
    }
}

TestMain

public class TestMain {
    public static void main(String[] args) {
        // 装了5个糖
        GumballMachine gumballMachine = new GumballMachine(5);
        System.out.println(gumballMachine + "
");// 打印糖果机信息

        gumballMachine.insertQuarter();// 投入硬币
        gumballMachine.turnCrank();// 转动曲柄        
        System.out.println(gumballMachine + "
");// 打印糖果机信息

        gumballMachine.insertQuarter();// 投入硬币
        gumballMachine.ejectQuarter();// 要求退币
        gumballMachine.turnCrank();// 转动曲柄,拿不到糖果
        System.out.println(gumballMachine + "
");// 打印糖果机信息

        gumballMachine.insertQuarter();// 投入硬币
        gumballMachine.turnCrank();// 转动曲柄(拿到糖果)
        gumballMachine.insertQuarter();// 投入硬币
        gumballMachine.turnCrank();// 转动曲柄(拿到糖果)
        gumballMachine.ejectQuarter();// 要求机器退钱
        System.out.println(gumballMachine + "
");// 打印糖果机信息

        gumballMachine.insertQuarter();// 投入硬币
        gumballMachine.insertQuarter();// 投入硬币
        gumballMachine.turnCrank();// 转动曲柄(拿到糖果)
        // 下面开始压力测试
        //gumballMachine.insertQuarter();// 投入硬币
        //gumballMachine.turnCrank();// 转动曲轴
        //gumballMachine.insertQuarter();// 投入硬币
        //gumballMachine.turnCrank();// 转动曲轴
        System.out.println(gumballMachine + "
");// 打印糖果机信息
    }
}

效果图(无压力测试)

效果图(压力测试)

效果可以快速实现,但这样的代码显然还有不足之处,假设有个新的需求:增加一个幸运用户,就是购买者有10%的概率可以一次买到两颗糖果,这又要如何实现呢?先看流程图

按照之前的1.0代码加新中奖者功能,显然不合适,要在每个方法里写入,显然太麻烦,这里就需要重构代码了。我们可以试着行为封装起来,也可以把糖果机器具体一下.我们要做的是如下:
1、首先定义一个State接口,在这个接口内,糖果机的每个动作都有一个对应的方法。
2、然后为机器中的每个状态实现类。这些类将负责在对应的状态下进行机器的行为。
3、重构旧代码,取而代之方式是将动作委托给状态类。

具体实现请看代码
结构图

State

package Interface;

public interface State {

    /**
     * 将四种状态抽象出来成基类
     */

    // 投入硬币
    public void insertQuarter();

    // 退回硬币
    public void ejectQuarter();

    // 转动曲柄
    public void turnCrank();

    // 发放糖果
    public void dispense();
}

GumballMachine

package Machine;

public class GumballMachine {
    /**
     * 不在使用静态整数,都是用对象
     * 
     */
    State soldOutState;// 糖果售空
    State noQuarterState;// 没有投币
    State hasQuarterState;// 投了币
    State soldState;// 糖果售出
    State winnerState;// 中奖状态
    // 当前状态,持有的是(糖果售空)对象
    State state = soldOutState;
    int count = 0;

    // numberGumball构造器取得糖果的初始数目后,并把它存放在一个实例变量中
    public GumballMachine(int numberGumballs) {
        soldOutState = new SoldOutState(this);
        noQuarterState = new NoQuarterState(this);
        hasQuarterState = new HasQuarterState(this);
        soldState = new SoldState(this);
        winnerState = new WinnerState(this);

        this.count = numberGumballs;
        if (numberGumballs > 0) {
            state = noQuarterState;
        }
    }

    /**
     * 机器的各个操作不在这里具体实现了 而是丢给接口,再让具体实现类去实现接口
     */
    // 添加硬币
    public void insertQuarter() {
        state.insertQuarter();
    }

    // 退出硬币
    public void ejectQuarter() {
        state.ejectQuarter();
    }

    // 使用曲柄
    public void turnCrank() {
        state.turnCrank();
        state.dispense();
    }

    // 变化状态
    public void setState(State state) {
        this.state = state;
    }

    // 糖果出货
    public void releaseBall() {
        System.out.println("一包糖果出来了");
        if (count != 0) {
            count = count - 1;
        }
    }

    public int getCount() {
        return count;
    }

    public State getState() {
        return state;
    }

    public State getSoldOutState() {
        return soldOutState;
    }

    public State getNoQuarterState() {
        return noQuarterState;
    }

    public State getHasQuarterState() {
        return hasQuarterState;
    }

    public State getSoldState() {
        return soldState;
    }

    public State getWinnerState() {
        return winnerState;
    }

    public String toString() {
        StringBuffer result = new StringBuffer();
        result.append("欢迎使用糖果机
");
        result.append("糖果数量:" + count + "
");
        result.append("当前糖果机状态: " + state + "
");
        return result.toString();
    }
}

各个行为类实现
HasQuarterState

package State_Implements;

public class HasQuarterState implements State {
    /**
     * 投币的实现类
     * 
     * @param gumballMachine
     */
    // 增加一个随机数产生器,10%机会
    Random randomWinner = new Random(System.currentTimeMillis());
    GumballMachine gumballMachine;

    public HasQuarterState(GumballMachine gumballMachine) {
        this.gumballMachine = gumballMachine;
    }

    public void insertQuarter() {
        System.out.println("你已经投过币了,不能再投了");
    }

    public void ejectQuarter() {
        System.out.println("硬币退出");
        // 退出硬币后,状态变为没有硬币(待购买)状态
        gumballMachine.setState(gumballMachine.getNoQuarterState());
    }

    public void turnCrank() {
        System.out.println("转动.....");
        int winner = randomWinner.nextInt(10);// 产生0-9随机数,当是0并且还有糖果的时候中奖了
        if ((winner == 0) && (gumballMachine.getCount() > 1)) {
            gumballMachine.setState(gumballMachine.getWinnerState());
        } else {
            gumballMachine.setState(gumballMachine.getSoldState());
        }
    }

    public void dispense() {
        System.out.println("没有糖果出来");
    }

    public String toString() {
        return "等待使用曲柄";
    }
}

NoQuarterState

package State_Implements;
/**
 * 没有投币状态实现,
 * 都通通要实现状态基类
 * @author Joy
 *
 */
public class NoQuarterState implements State {
    GumballMachine gumballMachine;
 
    public NoQuarterState(GumballMachine gumballMachine) {
        this.gumballMachine = gumballMachine;
    }
 
    public void insertQuarter() {
        System.out.println("你投了一个硬币");
        gumballMachine.setState(gumballMachine.getHasQuarterState());
    }
 
    public void ejectQuarter() {
        System.out.println("你没有投币,无法退币~~");
    }
 
    public void turnCrank() {
        System.out.println("你没有投币,无法继续~~");
     }
 
    public void dispense() {
        System.out.println("你需要投币才能买糖果");
    } 
 
    public String toString() {
        return "正在运营";
    }
}

SoldOutState

package State_Implements;

/**
 * 售空状态
 * 
 * @author Joy
 * 
 */
public class SoldOutState implements State {
    GumballMachine gumballMachine;
 
    public SoldOutState(GumballMachine gumballMachine) {
        this.gumballMachine = gumballMachine;
    }
 
    public void insertQuarter() {
        System.out.println("抱歉,你不能在投币了,糖果售空了");

    }
 
    public void ejectQuarter() {
        System.out.println("抱歉,糖果售空,无法退币");
    }
 
    public void turnCrank() {
        System.out.println("抱歉,转动曲柄无效,糖果售空了");

    }
 
    public void dispense() {
        System.out.println("糖果售空了");
    }
 
    public String toString() {
        return "糖果售空";
    }
}

SoldState

package State_Implements;
/**
 * 卖出糖果状态
 * 
 * @author Joy
 * 
 */
public class SoldState implements State {
    GumballMachine gumballMachine;
 
    public SoldState(GumballMachine gumballMachine) {
        this.gumballMachine = gumballMachine;
    }
       
    public void insertQuarter() {
        System.out.println("请稍等,糖果正出货");
    }
 
    public void ejectQuarter() {
        System.out.println("抱歉,你已使用曲柄,无法退币");
    }
 
    public void turnCrank() {
        System.out.println("曲柄不可重复使用");
    }
 
    public void dispense() {
        // 调用糖果出货方法
        gumballMachine.releaseBall();
        if (gumballMachine.getCount() > 0) {
            gumballMachine.setState(gumballMachine.getNoQuarterState());
        } else {
            System.out.println("哎呀,糖果售空了");
            gumballMachine.setState(gumballMachine.getSoldOutState());
        }
    }
 
    public String toString() {
        return "一个糖果已出货";
    }
}

WinnerState

package State_Implements;
public class WinnerState implements State {
    GumballMachine gumballMachine;
 
    public WinnerState(GumballMachine gumballMachine) {
        this.gumballMachine = gumballMachine;
    }
 
    public void insertQuarter() {
        System.out.println("不能投币");
    }
 
    public void ejectQuarter() {
        System.out.println("不能投币");
    }
 
    public void turnCrank() {
        System.out.println("不能使用曲柄");
    }
 
    public void dispense() {
        System.out.println("恭喜中奖了,你得到两个糖果~~");
        gumballMachine.releaseBall();
        // 此时糖果机器里只有一颗时候,那么第二颗就出不来,状态变为售空状态
        if (gumballMachine.getCount() == 0) {
            gumballMachine.setState(gumballMachine.getSoldOutState());
        } else {
            gumballMachine.releaseBall();
            if (gumballMachine.getCount() > 0) {
                gumballMachine.setState(gumballMachine.getNoQuarterState());
            } else {
                System.out.println("哎呀,糖果售空了");
                gumballMachine.setState(gumballMachine.getSoldOutState());
            }
        }
    }
 
    public String toString() {
        return "你是中奖者,得到两个糖果";
    }
}

GumballMachineTestDrive 测试类

package TestMain;

import Machine.GumballMachine;

public class GumballMachineTestDrive {

    public static void main(String[] args) {
        // 一开始5颗糖
        GumballMachine gumballMachine = new GumballMachine(5);
        System.out.println(gumballMachine + "
");

        gumballMachine.insertQuarter();
        gumballMachine.turnCrank();
        System.out.println(gumballMachine + "
");// 输出状态

        gumballMachine.insertQuarter();
        gumballMachine.turnCrank();
        gumballMachine.insertQuarter();
        gumballMachine.turnCrank();
        System.out.println(gumballMachine + "
");// 输出状态
    }
}

效果图


状态模式定义:允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
注:这个模式是将状态封装成为独立地类,并将动作委托给代表当前状态的对象。

状态模式类图如下,后来本人在回顾时发现状态模式和策略模式类图很相似,有兴趣朋友可以将两者去比较不同

感谢你看到这里,状态模式到这里就结束了,本人文笔随便,若有不足或错误之处望给予指点,90度弯腰~~~很快我会发布下一个设计模式的内容,生命不息,编程不止!

参考书籍:《Head First 设计模式》

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/67916.html

相关文章

  • 十一天-《企业应用架构模式》-对象-关系行为模式

    摘要:工作单元用于维护受业务事务影响的对象列表,并协调变化的写入和并发问题的解决。工作单元控制器工作单元控制所有数据库的读操作,一旦对象被读取,将将它注册为干净的对象。 1. 工作单元 用于维护受业务事务影响的对象列表,并协调变化的写入和并发问题的解决。如下: showImg(https://segmentfault.com/img/remote/1460000018095222?w=162...

    KavenFan 评论0 收藏0
  • 后台 - 收藏集 - 掘金

    摘要:探究系统登录验证码的实现后端掘金验证码生成类手把手教程后端博客系统第一章掘金转眼间时间就从月份到现在的十一月份了。提供了与标准不同的工作方式我的后端书架后端掘金我的后端书架月前本书架主要针对后端开发与架构。 Spring Boot干货系列总纲 | 掘金技术征文 - 掘金原本地址:Spring Boot干货系列总纲博客地址:http://tengj.top/ 前言 博主16年认识Spin...

    CrazyCodes 评论0 收藏0
  • 【全栈之路】JAVA基础课程十一_JDK8十大新特性(20190706v1.2)

    摘要:欢迎进入基础课程博客地址本系列文章将主要针对一些基础知识点进行讲解,为平时归纳所总结,不管是刚接触开发菜鸟还是业界资深人士,都希望对广大同行带来一些帮助。语法是,或者更一般的,要求构造器方法是没有参数静态方法引用。 欢迎进入JAVA基础课程 博客地址:https://blog.csdn.net/houjiyu...本系列文章将主要针对JAVA一些基础知识点进行讲解,为平时归纳所总结,...

    lcodecorex 评论0 收藏0
  • 【全栈之路】JAVA基础课程十一_JDK8十大新特性(20190706v1.2)

    摘要:欢迎进入基础课程博客地址本系列文章将主要针对一些基础知识点进行讲解,为平时归纳所总结,不管是刚接触开发菜鸟还是业界资深人士,都希望对广大同行带来一些帮助。语法是,或者更一般的,要求构造器方法是没有参数静态方法引用。 欢迎进入JAVA基础课程 博客地址:https://blog.csdn.net/houjiyu...本系列文章将主要针对JAVA一些基础知识点进行讲解,为平时归纳所总结,...

    VPointer 评论0 收藏0
  • 我的阿里之路+Java面经考点

    摘要:我的是忙碌的一年,从年初备战实习春招,年三十都在死磕源码,三月份经历了阿里五次面试,四月顺利收到实习。因为我心理很清楚,我的目标是阿里。所以在收到阿里之后的那晚,我重新规划了接下来的学习计划,将我的短期目标更新成拿下阿里转正。 我的2017是忙碌的一年,从年初备战实习春招,年三十都在死磕JDK源码,三月份经历了阿里五次面试,四月顺利收到实习offer。然后五月怀着忐忑的心情开始了蚂蚁金...

    姘搁『 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<