设计模式(二十二)——状态模式(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) 应用场景:当一个事件或者对象有很多种状态,状态之间会相互转换,对不同的状态要求有不同的行为的时候, 可以考虑使用状态模式
仅供参考,有错误还请指出!
有什么想法,评论区留言,互相指教指教。
觉得不错的可以点一下右边的推荐哟!
设计模式(二十二)——状态模式(APP抽奖活动+借贷平台源码剖析)的更多相关文章
- Java 设计模式系列(二十)状态模式
Java 设计模式系列(二十)状态模式 状态模式,又称状态对象模式(Pattern of Objects for States),状态模式是对象的行为模式.状态模式允许一个对象在其内部状态改变的时候改 ...
- 设计模式(二十一)——解释器模式(Spring 框架中SpelExpressionParser源码分析)
1 四则运算问题 通过解释器模式来实现四则运算,如计算 a+b-c 的值,具体要求 1) 先输入表达式的形式,比如 a+b+c-d+e, 要求表达式的字母不能重复 2) 在分别输入 a ,b, c, ...
- 设计模式(五)——原型模式(加Spring框架源码分析)
原型模式 1 克隆羊问题 现在有一只羊 tom,姓名为: tom, 年龄为:1,颜色为:白色,请编写程序创建和 tom 羊 属性完全相同的 10 只羊. 2 传统方式解决克隆羊问题 1) 思路分析(图 ...
- C#设计模式之十九状态模式(State Pattern)【行为型】
一.引言 今天我们开始讲"行为型"设计模式的第六个模式,该模式是[状态模式],英文名称是:State Pattern.无论是现实世界,还是面向对象的OO世界,里面都有一个东西, ...
- C#设计模式之十八状态模式(State Pattern)【行为型】
一.引言 今天我们开始讲“行为型”设计模式的第六个模式,该模式是[状态模式],英文名称是:State Pattern.无论是现实世界,还是面向对象的OO世界,里面都有一个东西,那就是对象.有对象当然就 ...
- 设计模式(十七)——迭代器模式(ArrayList 集合应用源码分析)
1 看一个具体的需求 编写程序展示一个学校院系结构:需求是这样,要在一个页面中展示出学校的院系组成,一个学校有多个学院, 一个学院有多个系.如图: 2 传统的设计方案(类图) 3 传统的方式的问题分析 ...
- Java设计模式(十) 备忘录模式 状态模式
(十九)备忘录模式 备忘录模式目的是保存一个对象的某个状态,在适当的时候恢复这个对象. class Memento{ private String value; public Memento(Stri ...
- 备忘录模式 Memento 快照模式 标记Token模式 行为型 设计模式(二十二)
备忘录模式 Memento 沿着脚印,走过你来时的路,回到原点. 苦海翻起爱恨 在世间难逃避命运 相亲竟不可接近 或我应该相信是缘份 一首<一生所爱>触动了多少 ...
- Java设计模式(十二) 策略模式
原创文章,同步发自作者个人博客,http://www.jasongj.com/design_pattern/strategy/ 策略模式介绍 策略模式定义 策略模式(Strategy Pattern) ...
随机推荐
- 面试时通过volatile关键字,全面展示线程内存模型的能力
面试时,面试官经常会通过volatile关键字来考核候选人在多线程方面的能力,一旦被问题此类问题,大家可以通过如下的步骤全面这方面的能力. 1 首先通过内存模型说明volatile关键字的作用 ...
- 低功耗降线性稳压器,24V转5V降压芯片
PW2330开发了一种高效率的同步降压DC-DC变换器3A输出电流.PW2330在4.5V到30V的宽输入电压范围内工作集成主开关和同步开关,具有非常低的RDS(ON)以最小化传导 损失.PW2330 ...
- 获取html中某些标签的值
一.获取单选按钮radio的值 <!doctype html> <html lang="en"> <head> <meta charset ...
- Redis持久化之父子进程与写时复制
之所以将Linux底层的写时复制技术放在Redis篇幅下,是因为Redis进行RDB持久化时,BGSAVE(后面称之为"后台保存")会开辟一个子进程,将数据从内存写进磁盘,这儿我产 ...
- PowerApps画布应用编码规范 和指南
花了一番功夫把PowerApps编码最佳实践的官方白皮书本地化了一下,顺便对部分产品变更做了校对和注释,欢迎大家查阅和分享. 从我实际的项目实施经验来讲,内容还是值得一读,可以帮助项目更好维护和管理. ...
- requestAnimationFrame小结
背景 在Web应用中,实现动画效果的方法比较多,Javascript 中可以通过定时器 setTimeout或者setInterval 来实现,css3 可以使用 transition 和 anima ...
- 渗透测试流程——渗透测试的9个步骤(转)
目录 明确目标 分析风险,获得授权 信息收集 漏洞探测(手动&自动) 漏洞验证 信息分析 利用漏洞,获取数据 信息整理 形成报告 1.明确目标 1)确定范围:测试的范围,如:IP.域名.内外网 ...
- 函数式编程(json、pickle、shelve)
本节内容 前言 json模块 pickle模块 shelve模块 总结 一.前言 1. 现实需求 每种编程语言都有各自的数据类型,其中面向对象的编程语言还允许开发者自定义数据类型(如:自定义类),Py ...
- java 生成xml
生成xml @Test public void parseApplicationConfigXML(){ try { SAXReader sax = new SAXReader(); Document ...
- OpenStack (云计算与openstck简介)
云计算 什么是云计算 云计算是一种按使用量付费的模式,这种模式提供可用的,便捷的,按需的网络访问,通过互联网进入可配置的计算资源共享池(资源包括,计算,存储,应用软件和服务) 云计算的特征 易于管理: ...