设计模式(二十二)——状态模式(APP抽奖活动+借贷平台源码剖析)

24.1 APP 抽奖活动问题

请编写程序完成 APP 抽奖活动 具体要求如下:

1) 假如每参加一次这个活动要扣除用户 50 积分,中奖概率是 10%

2) 奖品数量固定,抽完就不能抽奖

3) 活动有四个状态: 可以抽奖、不能抽奖、发放奖品和奖品领完

4) 活动的四个状态转换关系图(下图)

24.1 状态模式基本介绍

基本介绍

1) 状态模式(State Pattern):它主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题。状态和行为是一一对应的,状态之间可以相互转换

2) 当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类

24.2 状态模式的原理类图

  • 对原理类图的说明-即(状态模式的角色及职责)

1) Context 类为环境角色,  用于维护 State 实例,这个实例定义当前状态

2) State 是抽象状态角色,定义一个接口封装与 Context  的一个特点接口相关行为

3) ConcreteState 具体的状态角色,每个子类实现一个与 Context 的一个状态相关行为

24.1 状态模式解决 APP 抽奖问

1) 应用实例要求

完成 APP 抽奖活动项目,使用状态模式.

2) 思路分析和图解(类图)

  • 定义出一个接口叫状态接口,每个状态都实现它。
  • 接口有扣除积分方法、抽奖方法、发放奖品方法

3)代码实现

package com.lin.state;        /**     * 状态抽象类     *      * @author Administrator     *     */public abstract class State {    // 扣除积分 - 50    public abstract void deductMoney();    // 是否抽中奖品    public abstract boolean raffle();    // 发放奖品    public abstract void dispensePrize();}
package com.lin.state;/** * 抽奖活动 // * * @author Administrator * */public class RaffleActivity {    // state 表示活动当前的状态,是变化    State state = null;    // 奖品数量    int count = 0;    // 四个属性,表示四种状态    State noRafflleState = new NoRaffleState(this);    State canRaffleState = new CanRaffleState(this);    State dispenseState = new DispenseState(this);    State dispensOutState = new DispenseOutState(this);    // 构造器    // 1. 初始化当前的状态为 noRafflleState(即不能抽奖的状态)    // 2. 初始化奖品的数量    public RaffleActivity(int count) {        this.state = getNoRafflleState();        this.count = count;    }    // 扣分, 调用当前状态的 deductMoney    public void debuctMoney() {        state.deductMoney();    }    // 抽奖    public void raffle() {        // 如果当前的状态是抽奖成功        if (state.raffle()) {            // 领取奖品state.dispensePrize();        }    }    public State getState() {        return state;    }    public void setState(State state) {        this.state = state;    }    // 这里请大家注意,每领取一次奖品,count--    public int getCount() {        int curCount = count;        count--;        return curCount;    }    public void setCount(int count) {        this.count = count;    }    public State getNoRafflleState() {        return noRafflleState;    }    public void setNoRafflleState(State noRafflleState) {        this.noRafflleState = noRafflleState;    }    public State getCanRaffleState() {        return canRaffleState;    }    public void setCanRaffleState(State canRaffleState) {        this.canRaffleState = canRaffleState;    }    public State getDispenseState() {        return dispenseState;    }    public void setDispenseState(State dispenseState) {        this.dispenseState = dispenseState;    }    public State getDispensOutState() {        return dispensOutState;    }    public void setDispensOutState(State dispensOutState) {        this.dispensOutState = dispensOutState;    }}
package com.lin.state;    /**    *不能抽奖状态    *@author Administrator    *    */public class NoRaffleState extends State {    // 初始化时传入活动引用,扣除积分后改变其状态    RaffleActivity activity;    public NoRaffleState(RaffleActivity activity) {         this.activity = activity;    }    // 当前状态可以扣积分 , 扣除后,将状态设置成可以抽奖状态    @Override    public void deductMoney() {        System.out.println("扣除 50 积分成功,您可以抽奖了"); activity.setState(activity.getCanRaffleState());    }    // 当前状态不能抽奖    @Override    public boolean raffle() {        System.out.println("扣了积分才能抽奖喔!"); return false;    }    // 当前状态不能发奖品    @Override    public void dispensePrize() {        System.out.println("不能发放奖品");    }    }
package com.lin.state;    /**     * 发放奖品的状态     *      * @author Administrator     *     */public class DispenseState extends State {    // 初始化时传入活动引用,发放奖品后改变其状态    RaffleActivity activity;    public DispenseState(RaffleActivity activity) {        this.activity = activity;    }    //    @Override    public void deductMoney() {        System.out.println("不能扣除积分");    }    @Override    public boolean raffle() {        System.out.println("不能抽奖");        return false;    }    //发放奖品 @Override    public void dispensePrize() {        if (activity.getCount() > 0) {            System.out.println("恭喜中奖了");            // 改变状态为不能抽奖            activity.setState(activity.getNoRafflleState());        } else {            System.out.println("很遗憾,奖品发送完了");            // 改变状态为奖品发送完毕, 后面我们就不可以抽奖            activity.setState(activity.getDispensOutState());            //System.out.println("抽奖活动结束");            //System.exit(0);        }    }}
package com.lin.state;    /**     * 奖品发放完毕状态 说明,当我们 activity 改变成 DispenseOutState, 抽奖活动结束     *      * @author Administrator     *     */public class DispenseOutState extends State {    // 初始化时传入活动引用    RaffleActivity activity;    public DispenseOutState(RaffleActivity activity) {        this.activity = activity;    }    @Override    public void deductMoney() {        System.out.println("奖品发送完了,请下次再参加");    }    @Override    public boolean raffle() {        System.out.println("奖品发送完了,请下次再参加");        return false;    }    @Override    public void dispensePrize() {        System.out.println("奖品发送完了,请下次再参加");    }}
package com.lin.state;import java.util.Random;/** * 可以抽奖的状态 *  * @author Administrator * */public class CanRaffleState extends State {    RaffleActivity activity;    public CanRaffleState(RaffleActivity activity) {        this.activity = activity;    }    // 已经扣除了积分,不能再扣 @Override    public void deductMoney() {        System.out.println("已经扣取过了积分");    }    // 可以抽奖, 抽完奖后,根据实际情况,改成新的状态    @Override    public boolean raffle() {        System.out.println("正在抽奖,请稍等!");        Random r = new Random();        int num = r.nextInt(10);        // 10%中奖机会        if (num == 0) {            // 改 变 活 动 状 态 为 发 放 奖 品 context             activity.setState(activity.getDispenseState());            return true;        } else {            System.out.println("很遗憾没有抽中奖品!");            // 改变状态为不能抽奖            activity.setState(activity.getNoRafflleState());            return false;        }    }    // 不能发放奖品    @Override    public void dispensePrize() {        System.out.println("没中奖,不能发放奖品");    }}
package com.lin.state;    /**     * 状态模式测试类     *      * @author Administrator     *     */public class Client {    public static void main(String[] args) {        // 创建活动对象,奖品有 1 个奖品        RaffleActivity activity = new RaffleActivity(1);        // 我们连续抽 300 次奖        for (int i = 0; i < 40; i++) {            System.out.println("--------第" + (i + 1) + "次抽奖----------");            // 参加抽奖,第一步点击扣除积分            activity.debuctMoney();            // 第二步抽奖            activity.raffle();        }    }}

24.1 状态模式在实际项目-借贷平台 源码剖析

1) 借贷平台的订单,有审核-发布-抢单 等等 步骤,随着操作的不同,会改变订单的状态, 项目中的这个模块实现就会使用到状态模式

2) 通常通过 if/else 判断订单的状态,从而实现不同的逻辑,伪代码如下

3)代码实现

package com.lin.state.project;/** * 状态接口 *  * @author Administrator * */public interface State {    /**     * 电 审     */    void checkEvent(Context context);    /**     * 电审失败     */    void checkFailEvent(Context context);    /**     * 定价发布     */    void makePriceEvent(Context context);    /**     * 接 单     *      *      */    void acceptOrderEvent(Context context);    /**     * 无人接单失效     */    void notPeopleAcceptEvent(Context context);    /**     * 付 款     */    void payOrderEvent(Context context);    /**     * 接单有人支付失效     */    void orderFailureEvent(Context context);    /**     * 反 馈     */    void feedBackEvent(Context context);    String getCurrentState();}
package com.lin.state.project;    /**     * 状态枚举类     *      * @author Administrator     *     */public enum StateEnum {    //订单生成    GENERATE(1, "GENERATE"),    //已审核    REVIEWED(2, "REVIEWED"),        //已发布    PUBLISHED(3, "PUBLISHED"),    //待付款    NOT_PAY(4, "NOT_PAY"),    //已付款    PAID(5, "PAID"),    //已完结    FEED_BACKED(6, "FEED_BACKED");    private int key;    private String value;    StateEnum(int key, String value) {        this.key = key;        this.value = value;    }    public int getKey() {        return key;    }    public String getValue() {        return value;    }}
package com.lin.state.project;public abstract class AbstractState implements State {    protected static final RuntimeException EXCEPTION = new RuntimeException("操作流程不允许");    //抽象类,默认实现了 State 接口的所有方法    //该类的所有方法,其子类(具体的状态类),可以有选择的进行重写    @Override    public void checkEvent(Context context) {        throw EXCEPTION;    }    @Override    public void checkFailEvent(Context context) {        throw EXCEPTION;    }    @Override    public void makePriceEvent(Context context) {        throw EXCEPTION;    }    @Override    public void acceptOrderEvent(Context context) {        throw EXCEPTION;    }    @Override    public void notPeopleAcceptEvent(Context context) {        throw EXCEPTION;    }    @Override    public void payOrderEvent(Context context) {        throw EXCEPTION;    }    @Override    public void orderFailureEvent(Context context) {        throw EXCEPTION;    }    @Override    public void feedBackEvent(Context context) {        throw EXCEPTION;    }}
package com.lin.state.project;    //环境上下文public class Context extends AbstractState {    //当前的状态 state, 根据我们的业务流程处理,不停的变化    private State state;    @Override    public void checkEvent(Context context) {        state.checkEvent(this);        getCurrentState();    }    @Override    public void checkFailEvent(Context context) {        state.checkFailEvent(this);        getCurrentState();    }    @Override    public void makePriceEvent(Context context) {        state.makePriceEvent(this);        getCurrentState();    }    @Override    public void acceptOrderEvent(Context context) {        state.acceptOrderEvent(this);        getCurrentState();    }    @Override    public void notPeopleAcceptEvent(Context context) {        state.notPeopleAcceptEvent(this);        getCurrentState();    }    @Override    public void payOrderEvent(Context context) {        state.payOrderEvent(this);        getCurrentState();    }    @Override    public void orderFailureEvent(Context context) {        state.orderFailureEvent(this);        getCurrentState();    }    @Override    public void feedBackEvent(Context context) {        state.feedBackEvent(this);        getCurrentState();    }    public State getState() {        return state;    }    public void setState(State state) {        this.state = state;    }    @Override    public String getCurrentState() {        System.out.println("当前状态 : " + state.getCurrentState());        return state.getCurrentState();    }}
package com.lin.state.project;    //各种具体状态类public class FeedBackState extends AbstractState {    @Override    public String getCurrentState() {        return StateEnum.FEED_BACKED.getValue();    }}class GenerateState extends AbstractState {    @Override    public void checkEvent(Context context) {        context.setState(new ReviewState());    }    @Override    public void checkFailEvent(Context context) {        context.setState(new FeedBackState());    }    @Override    public String getCurrentState() {        return StateEnum.GENERATE.getValue();    }}class NotPayState extends AbstractState {    @Override    public void payOrderEvent(Context context) {        context.setState(new PaidState());    }    @Override    public void feedBackEvent(Context context) {        context.setState(new FeedBackState());    }    @Override    public String getCurrentState() {        return StateEnum.NOT_PAY.getValue();    }}class PaidState extends AbstractState {    @Override    public void feedBackEvent(Context context) {        context.setState(new FeedBackState());    }    @Override    public String getCurrentState() {        return StateEnum.PAID.getValue();    }}class PublishState extends AbstractState {    @Override    public void acceptOrderEvent(Context context) {    //把当前状态设置为    NotPayState。。。    //至于应该变成哪个状态,有流程图来决定        context.setState(new NotPayState());    }    @Override    public void notPeopleAcceptEvent(Context context) {        context.setState(new FeedBackState());    }    @Override    public String getCurrentState() {        return StateEnum.PUBLISHED.getValue();    }}class ReviewState extends AbstractState {    @Override    public void makePriceEvent(Context context) {        context.setState(new PublishState());    }    @Override    public String getCurrentState() {        return StateEnum.REVIEWED.getValue();    }}
package com.lin.state.project;    /** 测试类 */public class Client {    public static void main(String[] args) {        //创建 context 对象        Context context = new Context();        //将当前状态设置为 PublishState         context.setState(new PublishState());        System.out.println(context.getCurrentState());        //        //publish --> not pay         context.acceptOrderEvent(context);        //        //not pay --> paid         context.payOrderEvent(context);        //    // 失败, 检测失败时,会抛出异常        //    try {        //    context.checkFailEvent(context);        //    System.out.println("流程正常..");        //    } catch (Exception e) {                //    // TODO: handle exception        //    System.out.println(e.getMessage());        //    }    }}

24.1 状态模式的注意事项和细节

1) 代码有很强的可读性。状态模式将每个状态的行为封装到对应的一个类中

2) 方便维护。将容易产生问题的 if-else 语句删除了,如果把每个状态的行为都放到一个类中,每次调用方法时都要判断当前是什么状态,不但会产出很多 if-else 语句,而且容易出错

3) 符合“开闭原则”。容易增删状态

4) 会产生很多类。每个状态都要一个对应的类,当状态过多时会产生很多类,加大维护难度

5) 应用场景:当一个事件或者对象有很多种状态,状态之间会相互转换,对不同的状态要求有不同的行为的时候, 可以考虑使用状态模式

仅供参考,有错误还请指出!

有什么想法,评论区留言,互相指教指教。

觉得不错的可以点一下右边的推荐哟!

(0)

相关推荐