设计模式(二十二)——状态模式(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) ...
随机推荐
- Windows程序通用自动更新模块(C#,.NET4.5以上)
本通用自动更新模块适合所有Windows桌面程序的自动更新,不论语言,无论Winform还是wpf. 一.工作流程:1. 主程序A调起升级程序B2. B从服务器获取更新程序列表,打印更新信息.3. B ...
- [Usaco2016 Dec]Counting Haybales
原题链接https://www.lydsy.com/JudgeOnline/problem.php?id=4747 先将原数组排序,然后二分查找即可.时间复杂度\(O((N+Q)logN)\). #i ...
- 安装git-macOS系统
通过homebrew安装Git 1.安装homebrew /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/H ...
- 手把手做一个基于vue-cli的组件库(下篇)
基于vue-cli4的ui组件库,上篇:如何做一个初步的组件.下篇:编写说明文档及页面优化.接上篇,开工. GitHub源码地址:https://github.com/sq-github/sq-ui ...
- Promise用法
1.概述 Promise是一步编程的一种解决方案,从语法上讲,promise是一个对象,从它可以获取异步的问题 Promise的优点: 可以避免多次异步调用嵌套导致的回调地域 提供了简洁的api,使得 ...
- 内存模型 Memory model 内存分布及程序运行中(BSS段、数据段、代码段、堆栈
C语言中内存分布及程序运行中(BSS段.数据段.代码段.堆栈) - 秦宝艳的个人页面 - 开源中国 https://my.oschina.net/pollybl1255/blog/140323 Mem ...
- Centos 7 Redis 安装并开机启动
一.下载并编译 cd /usr/local/bin目录可以自己选,我将redis安装到/usr/local/bin目录下.wget http://download.redis.io/releases/ ...
- mysql查询数据库中是否存在某个字段
select table_name from information_schema.columns where table_schema = '库名' and column_name='字段名';
- Java 类的加载与初始化
本文结构: 1.先看几道题 2.类的加载于初始化 (1)类的加载 (2)类的初始化 (a)会发生类的初始化的情况 (b)不会发生类的初始化的情况 首先看几道题. 解析可在看完讲解后再看 Demo1 p ...
- mysql和oracle的字符拼接方法
不同的数据库,相应的字符串拼接方式不同,通过对比加深一下记忆. 一.MySQL字符串拼接 1.CONCAT函数 语法格式:CONCAT(char c1, char c2, ..., char cn) ...