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

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

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

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

设计模式(二十二)——状态模式(APP抽奖活动+借贷平台源码剖析)的更多相关文章

  1. Java 设计模式系列(二十)状态模式

    Java 设计模式系列(二十)状态模式 状态模式,又称状态对象模式(Pattern of Objects for States),状态模式是对象的行为模式.状态模式允许一个对象在其内部状态改变的时候改 ...

  2. 设计模式(二十一)——解释器模式(Spring 框架中SpelExpressionParser源码分析)

    1 四则运算问题 通过解释器模式来实现四则运算,如计算 a+b-c 的值,具体要求 1) 先输入表达式的形式,比如 a+b+c-d+e,  要求表达式的字母不能重复 2) 在分别输入 a ,b, c, ...

  3. 设计模式(五)——原型模式(加Spring框架源码分析)

    原型模式 1 克隆羊问题 现在有一只羊 tom,姓名为: tom, 年龄为:1,颜色为:白色,请编写程序创建和 tom 羊 属性完全相同的 10 只羊. 2 传统方式解决克隆羊问题 1) 思路分析(图 ...

  4. C#设计模式之十九状态模式(State Pattern)【行为型】

    一.引言   今天我们开始讲"行为型"设计模式的第六个模式,该模式是[状态模式],英文名称是:State Pattern.无论是现实世界,还是面向对象的OO世界,里面都有一个东西, ...

  5. C#设计模式之十八状态模式(State Pattern)【行为型】

    一.引言 今天我们开始讲“行为型”设计模式的第六个模式,该模式是[状态模式],英文名称是:State Pattern.无论是现实世界,还是面向对象的OO世界,里面都有一个东西,那就是对象.有对象当然就 ...

  6. 设计模式(十七)——迭代器模式(ArrayList 集合应用源码分析)

    1 看一个具体的需求 编写程序展示一个学校院系结构:需求是这样,要在一个页面中展示出学校的院系组成,一个学校有多个学院, 一个学院有多个系.如图: 2 传统的设计方案(类图) 3 传统的方式的问题分析 ...

  7. Java设计模式(十) 备忘录模式 状态模式

    (十九)备忘录模式 备忘录模式目的是保存一个对象的某个状态,在适当的时候恢复这个对象. class Memento{ private String value; public Memento(Stri ...

  8. 备忘录模式 Memento 快照模式 标记Token模式 行为型 设计模式(二十二)

    备忘录模式 Memento   沿着脚印,走过你来时的路,回到原点.     苦海翻起爱恨   在世间难逃避命运   相亲竟不可接近   或我应该相信是缘份   一首<一生所爱>触动了多少 ...

  9. Java设计模式(十二) 策略模式

    原创文章,同步发自作者个人博客,http://www.jasongj.com/design_pattern/strategy/ 策略模式介绍 策略模式定义 策略模式(Strategy Pattern) ...

随机推荐

  1. 入门OJ:售货员的难题

    题目描述 某乡有n个村庄(1<n<15),有一个售货员,他要到各个村庄去售货,各村庄之间的路程s(0<s<1000)是已知的,且A村到B村与B村到A村的路大多不同.为了提高效率 ...

  2. Junit测试和反射

    Junit单元测试 测试分类 黑盒测试:不需要写代码,给输入值,看程序能否得到输出期望值. 白盒测试:需要些代码,关注程序具体的执行流程. Junit的使用 步骤 定义一个测试类(测试用例). 定义测 ...

  3. 【Android】编译报错 Annotation processors must be explicitly declared now 解决方案

    问题 在网上下载一个demo,因为版本久远,里面添加了本地 Butter Knife 的jar包,在编译时报错 Annotation processors must be explicitly dec ...

  4. 使用fdopen对python进程产生的文件进行权限最小化配置

    需求背景 用python进行文件的创建和读写操作时,我们很少关注所创建的文件的权限配置.对于一些安全性较高的系统,如果我们创建的文件权限其他用户或者同一用户组里的其他用户有可读权限的话,有可能导致不必 ...

  5. 转 1 认识开源性能测试工具jmeter

    1 认识开源性能测试工具jmeter   典型的性能测试工具主要有2个,Load Runner和jmeter.Load Runner是商业化的,Jmeter是开源的.下面我们认识一下开源性能测试工具j ...

  6. 从零开始学spring源码之ioc预热:bean的拓展和beanProcessor注册

    上篇聊完了bean的解析,说起来做的事情很简单,把xml文件里面配置的标签全部解析到spring容器里面,但是spring做的时候,花了那么大代价去做,后面看看到底值不值得呢. 接下来看看prepar ...

  7. Covering Indexes in MySQL, PostgreSQL, and MongoDB

    Covering Indexes in MySQL, PostgreSQL, and MongoDB - Orange Matter https://orangematter.solarwinds.c ...

  8. java定时任务之Qutaz

    前不久一直在学习Qztaz,干好写了一个案例分享一下给大家,希望大家可以用得到. 那么现在开始吧, 一:什么事Qutaz? Quartz是OpenSymphony开源组织在Job scheduling ...

  9. 【DB宝36】使用Docker分分钟搭建漂亮的prometheus+grafana监控

    目录 一.部署Prometheus+grafana环境 1.1.下载Prometheus+grafana镜像 1.2.创建镜像 1.3.浏览器访问 二.监控Linux主机 2.1.在被监控主机上部署n ...

  10. 获取本机IP和主机名

    如果是在windows环境: 使用InetAddress.getLocalHost()方法即可 package com.datongsoft.wg.common.util; import java.n ...