APP 抽奖活动问题
请编写程序完成 APP 抽奖活动具体要求如下:
- 1)假如每参加一次这个活动要扣除用户 50 积分,中奖概率是 10%
- 2)奖品数量固定,抽完就不能抽奖
- 3)活动有四个状态:可以抽奖、不能抽奖、发放奖品和奖品领完
- 4)活动的四个状态转换关系图
状态模式基本介绍
- 1)状态模式(State Pattern):它主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题。状态和行为是一一对应的,状态之间可以相互转换
- 2)当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类
原理类图
角色与职责
Context
环境角色:维护一个State
实例,这个实例定义了当前状态
State
抽象状态角色:定义一个接口,封装与Context
的一个特点接口相关行为
ConcreteState
具体状态角色:实现一个与Context
的一个状态相关行为
状态模式解决 APP 抽奖问题
UML 类图
核心代码
抽象状态角色
1 2 3 4 5 6 7 8 9 10
|
public interface State { Boolean reduceMoney();
Boolean raffle();
Boolean dispensePrize(); }
|
不能抽奖状态类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
|
public class NoRaffleState implements State { private RaffleActivity raffleActivity; private int integral = 100;
public NoRaffleState(RaffleActivity raffleActivity) { this.raffleActivity = raffleActivity; }
@Override public Boolean reduceMoney() { if (integral < 50) { System.out.println("您的积分余额不足~"); return false; } integral -= 50; raffleActivity.setCanRaffleState(); System.out.println("已扣除50积分,可以进行抽奖啦~"); return true; }
@Override public Boolean raffle() { System.out.println("当前无法进行抽奖~"); return false; }
@Override public Boolean dispensePrize() { System.out.println("当前无法领取奖品~"); return false; } }
|
可以抽奖状态类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
|
public class CanRaffleState implements State { private RaffleActivity raffleActivity;
public CanRaffleState(RaffleActivity raffleActivity) { this.raffleActivity = raffleActivity; }
@Override public Boolean reduceMoney() { System.out.println("已扣除50积分,可以进行抽奖啦~"); return false; }
@Override public Boolean raffle() { if (new Random().nextInt(10) == 0) { raffleActivity.setDispenseState(); System.out.println("恭喜您,中奖了~"); return true; } raffleActivity.setNoRaffleState(); System.out.println("很遗憾,您没有中奖~"); return false; }
@Override public Boolean dispensePrize() { System.out.println("尚未进行抽奖,无法领取奖品!"); return false; } }
|
发放奖品状态类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
|
public class DispenseState implements State { private RaffleActivity raffleActivity;
public DispenseState(RaffleActivity raffleActivity) { this.raffleActivity = raffleActivity; }
@Override public Boolean reduceMoney() { System.out.println("已经进行过抽奖啦!"); return false; }
@Override public Boolean raffle() { System.out.println("已经进行过抽奖啦!"); return false; }
@Override public Boolean dispensePrize() { if (raffleActivity.getCount() <= 0) { raffleActivity.setDispenseOutState(); System.out.println("今日奖品已领完,明天再来吧~"); return false; } raffleActivity.setNoRaffleState(); System.out.println("奖品领取成功~"); return true; } }
|
奖品领完状态类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
public class DispenseOutState implements State { private RaffleActivity raffleActivity;
public DispenseOutState(RaffleActivity raffleActivity) { this.raffleActivity = raffleActivity; }
@Override public Boolean reduceMoney() { System.out.println("今日奖品已领完,明天再来吧~"); return false; }
@Override public Boolean raffle() { System.out.println("今日奖品已领完,明天再来吧~"); return false; }
@Override public Boolean dispensePrize() { System.out.println("今日奖品已领完,明天再来吧~"); return false; } }
|
测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| RaffleActivity raffleActivity = new RaffleActivity(2);
System.out.println("======第一次抽奖======"); raffleActivity.raffle();
System.out.println("======第二次抽奖======"); raffleActivity.raffle();
System.out.println("======第三次抽奖======"); raffleActivity.raffle();
|
状态模式在实际项目——借贷平台源码剖析
1 2 3 4 5 6 7
| if(审核){ }else if(发布){ }else if(接单){ }
|
问题分析:这类代码难以应对变化,在添加一种状态时,我们需要手动添加if/else
,在添加一种功能时,要对所有的状态进行判断。因此代码会变得越来越臃肿,并且一旦没有处理某个状态,便会发生极其严重的 BUG,难以维护
- 3)使用状态模式完成借贷平台项目的审核模块 [设计+代码]
设计
借贷平台—流程审批
状态模式本质上是一种基于状态和事件的状态机,下面是订单流程的状态图
通过状态图,我们再设计一张横纵坐标关系表来比较,如下图
状态 \ 事件 |
电审(1) |
电审失败(2) |
定价发布(3) |
接单(4) |
接单失败(5) |
付款(6) |
支付失效(7) |
反馈(8) |
订单生成(1) |
已审核(2) |
已完结(6) |
|
|
|
|
|
|
已审核(2) |
|
|
已发布(3) |
|
|
|
|
|
已发布(3) |
|
|
|
待付款(4) |
已完结(6) |
|
|
|
待付款(4) |
|
|
|
|
|
已付款(5) |
已完结(6) |
|
已付款(5) |
|
|
|
|
|
|
|
已完结(6) |
已完结(6) |
|
|
|
|
|
|
|
|
UML 类图
代码
状态接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
|
public interface State {
void electronicAudit(Context context);
void electronicAuditFail(Context context);
void releasePricing(Context context);
void acceptOrder(Context context);
void acceptOrderFail(Context context);
void payMoney(Context context);
void feedback(Context context);
String getCurrentState(); }
|
抽象状态类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
|
public abstract class AbstractState implements State { private static final RuntimeException EXCEPTION = new RuntimeException("操作流程有误");
@Override public void electronicAudit(Context context) { throw EXCEPTION; }
@Override public void electronicAuditFail(Context context) { throw EXCEPTION;
}
@Override public void releasePricing(Context context) { throw EXCEPTION; }
@Override public void acceptOrder(Context context) { throw EXCEPTION; }
@Override public void acceptOrderFail(Context context) { throw EXCEPTION; }
@Override public void payMoney(Context context) { throw EXCEPTION; }
@Override public void feedback(Context context) { throw EXCEPTION; } }
|
具体状态类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
|
public class GeneratedState extends AbstractState { @Override public void electronicAudit(Context context) { context.setState(new AuditedState()); }
@Override public void electronicAuditFail(Context context) { context.setState(new FinishedState()); }
@Override public String getCurrentState() { return StateEnum.GENERATED.name(); } }
public class AuditedState extends AbstractState { @Override public void releasePricing(Context context) { context.setState(new PublishedState()); }
@Override public String getCurrentState() { return StateEnum.AUDITED.name(); } }
public class PublishedState extends AbstractState { @Override public void acceptOrder(Context context) { context.setState(new NotPaidState()); }
@Override public void acceptOrderFail(Context context) { context.setState(new FinishedState()); }
@Override public String getCurrentState() { return StateEnum.PUBLISHED.name(); } }
public class NotPaidState extends AbstractState { @Override public void payMoney(Context context) { context.setState(new PaidState()); }
@Override public void feedback(Context context) { context.setState(new FinishedState()); }
@Override public String getCurrentState() { return StateEnum.NOT_PAID.name(); } }
public class PaidState extends AbstractState { @Override public void feedback(Context context) { context.setState(new FinishedState()); }
@Override public String getCurrentState() { return StateEnum.PAID.name(); } }
public class FinishedState extends AbstractState { @Override public String getCurrentState() { return StateEnum.FINISHED.name(); } }
|
状态枚举类
1 2 3 4 5 6 7 8
| public enum StateEnum { GENERATED, AUDITED, PUBLISHED, NOT_PAID, PAID, FINISHED; }
|
上下文环境类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
|
public class Context extends AbstractState { private State state;
public Context() { state = new GeneratedState(); }
@Override public void electronicAudit(Context context) { state.electronicAudit(context); getCurrentState(); }
@Override public void electronicAuditFail(Context context) { state.electronicAuditFail(context); getCurrentState(); }
@Override public void releasePricing(Context context) { state.releasePricing(context); getCurrentState(); }
@Override public void acceptOrder(Context context) { state.acceptOrder(context); getCurrentState(); }
@Override public void acceptOrderFail(Context context) { state.acceptOrderFail(context); getCurrentState(); }
@Override public void payMoney(Context context) { state.payMoney(context); getCurrentState(); }
@Override public void feedback(Context context) { state.feedback(context); 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(); } }
|
测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| Context context = new Context(); context.electronicAudit(context); context.releasePricing(context); context.acceptOrder(context); context.payMoney(context);
context.electronicAuditFail(context); context.acceptOrderFail(context);
|
状态模式的注意事项和细节
优点
- 1)代码有很强的可读性:状态模式将每个状态的行为封装到对应的一个类中
- 2)方便维护:将容易产生问题的
if-else
语句删除了,如果把每个状态的行为都放到一个类中,每次调用方法时都要判断当前是什么状态,不但会产出很多if-else
语句,而且容易出错
- 3)符合开闭原则,容易增删状态
缺点
- 4)会产生很多类:每个状态都要一个对应的类,当状态过多时会产生很多类,加大维护难度
应用场景
- 5)当一个事件或者对象有很多种状态,状态之间会相互转换,对不同的状态要求有不同的行为时,可以考虑使用状态模式