状态模式 State 概述
基本介绍
- 状态模式主要用来解决对象在多种状态之间转换时,不同状态,行为不同的问题,当一个对象的内在状态发生改变时,该对象的行为也会发送相应的改变,在传统模式下通常使用一个字段来标识一个类的状态,然后通过逻辑代码去判断获取相应的状态,执行相应的操作,假设后续更改添加状态需要修改逻辑代码
- 状态模式的适用场景: 一个对象存在多个状态,且状态之间可以相互转换,不同状态行为不同时
- 状态模式的优点: 每个状态对应一个类,不同状态之间相互隔离,把状态的转换逻辑放入不同的状态类中,减少相互之间的依赖,增加新的状态非常简单,利于扩展
- 缺点: 状态过多时,需要创建多个状态类
状态模式角色分析
- 上下文环境 Context:与客户端的交互层,包含执行功能所需要的所有数据,持有所有状态对象,与当前状态对象,提供执行功能的方法,功能的执行实际是通过持有的当前状态对象来调用执行的
- 抽象状态 State:持有上下文环境对象,创建具体状态对象,需要初始化赋值持有的上下文环境对象,这个上下文环境对象代表的是当前,提供执行功能的抽象方法
- 具体状态 ConcreteState :具体状态一般存在多个,实现抽象状态,根据不同状态执行功能的不同,重写功能方法,在状态发生改变时通过持有的上下文环境对象,获取上下文中持有的对应的状态,赋值给上下文环境对象中的当前状态即可,当前状态就发生了改变,后续客户端通过上下文环境对象执行功能时,获取当前状态也就是改变后的状态执行对应的功能
自己理解的状态模式
上下文环境对象与状态对象相互持有对方,上下文中通过持有的当前状态对象执行功能方法,状态对象中,通过持有上下文对象,修改当前状态
示例
案例
客户贷款,贷款首先要试算额度,当有额度时,可以申请贷款,申请贷款额度不可以超过试算额度,等待放款,还款,假设客户试算额度为20万,实际贷款为10万,客户可以不试算额度,继续发起贷款申请,但客户额度使用完毕,若想再次贷款,需要重新试算额度,重新申请,重新试算额度需要偿清前面的所有贷款
分析案例(不是太严谨,只关注设计模式)
- 功能: 额度试算, 申请贷款, 放款, 还款
- 涉及到两个数据,试算额度,贷款额度这就是上
- 状态: 试算额度状态,可以发起额度试算,获取额度,申请贷款状态,可以发起贷款申请,等待放款状态,还款状态,可以还款,可以再次发起贷款申请
代码
- 根据公共数据,所有的状态,执行的功能,创建与客户端的交互层上下文环境对象
class ActivityContext{
//当前的状态(是变化的)
private State state = null;
//当前额度
private int limit;
//当前实际贷款(欠款)
private int debt;
//持有所有状态,状态对象中持有的数据是当前上下文对象本身)
private State applyLimitState = new ApplyLimitState(this);
private State applyLoansState = new ApplyLoansState(this);
private State awaitState = new AwaitState(this);
private State repaymentState = new RepaymentState(this);
//构造器,在创建上下文对象时,给出初始化状态
public ActivityContext() {
this.state = applyLimitState;
}
//状态转换,修改当前状态
public void convertState(State state){
this.state =state;
}
//定义执行功能的方法,实际是通过持有的当前
//状态对象调用执行的
//试算额度
public void applyLimit(int limit){
state.applyLimit(limit);
};
//申请贷款
public void applyLoans(int loansLimit){
state.applyLoans(loansLimit);
};
//放款
public void grant() {
state.grant();
}
//还款
public void repayment(int repaymentLimit){
state.repayment(repaymentLimit);
};
//提供get ,set方法
public int getLimit() {
return limit;
}
public void setLimit(int limit) {
this.limit = limit;
}
public int getDebt() {
return debt;
}
public void setDebt(int debt) {
this.debt = debt;
}
public void setState(State state){
this.state = state;
}
public State getApplyLimitState() {
return applyLimitState;
}
public State getApplyLoansState() {
return applyLoansState;
}
public State getAwaitState() {
return awaitState;
}
public State getRepaymentState() {
return repaymentState;
}
}
- 根据功能,创建抽象状态父接口,提供抽象功能方法,在具体状态中,状态不同功能不同进行具体实现
abstract class State{
//持有上下文环境对象
public ActivityContext activityContext;
public State(ActivityContext activityContext){
this.activityContext = activityContext;
}
//试算额度
public abstract void applyLimit(int limit);
//申请贷款
public abstract void applyLoans(int loansLimit);
//放款
public abstract void grant();
//还款
public abstract void repayment(int repaymentLimit);
}
- 创建具体状态
试算额度状态,额度生成后通过持有的上下文环境对象,修改状态为"申请贷款状态"
//试算额度状态(也可看为初始化状态)
class ApplyLimitState extends State {
//创建具体状态对象需要获取上下文环境,也就是对产品相关数据的初始化
public ApplyLimitState(ActivityContext activityContext) {
super(activityContext);
}
//额度试算方法(演示代码,为了方便直接将试算额度传递进来
//limit看为试算生成的额度)
@Override
public void applyLimit(int limit) {
//设置上下文环境中的当前额度
activityContext.setLimit(limit);
//额度试算成功后,通过持有的上下文环境对象
//将状态转换为支持申请贷款的"申请贷款"状态
activityContext.convertState(activityContext.getApplyLoansState());
System.out.println("试算额度为"+limit+"万元");
}
//申请贷款方法,此时为"试算额度"状态,执行申请贷款,提示失败
@Override
public void applyLoans(int loansLimit) {
System.out.println("当前状态没有额度,请先试算额度,然后申请贷款");
}
@Override
public void grant() {
System.out.println("当前状态没有额度,请先试算额度,然后申请贷款");
}
@Override
public void repayment(int repaymentLimit) {
System.out.println("当前没有欠款!");
}
}
申请贷款状态,申请贷款成功后,通过持有的上下文环境修改当前状态为"待放款状态"
class ApplyLoansState extends State {
public ApplyLoansState(ActivityContext activityContext) {
super(activityContext);
}
@Override
public void applyLimit(int limit) {
System.out.println("当前状态额度已生成!");
}
//申请贷款方法,loansLimit申请贷款金额,
//申请金额应该小于等于试算金额,当申请成功时
//计算: 剩余额度=试算额度-申请实际贷款额度
//将申请金额赋值给debt,模式贷款后的欠款
//申请成功后将当前状态修改为支持放款的"等待放款"状态
@Override
public void applyLoans(int loansLimit) {
//获取试算额度
int limit = activityContext.getLimit();
//试算额度与实际申请贷款额度绝对值
int val = Math.abs(limit-loansLimit);
if(limit < loansLimit){
System.out.println("申请贷款金额有误!金额超过试算额度:" + val);
return;
}
activityContext.setLimit(val);
activityContext.setDebt(activityContext.getDebt()+loansLimit);
//修改当前状态
activityContext.convertState(activityContext.getAwaitState());
System.out.println("申请贷款实际金额为:" +loansLimit+"万元,剩余额度:" +val);
}
@Override
public void grant() {
System.out.println("没有贷款申请,请提交贷款申请!");
}
@Override
public void repayment(int repaymentLimit) {
System.out.println("当前没有欠款!");
}
}
等待放款状态,执行放款成功后,通过持有的上下文环境对象修改当前状态为还款状态
//等待放款状态(模式,并不是真实案例,实际可能还需要有
//"放款成功"与"放款失败"状态,并且放款操作应该有另一方来执行的
class AwaitState extends State {
public AwaitState(ActivityContext activityContext) {
super(activityContext);
}
@Override
public void applyLimit(int limit) {
System.out.println("等待放款状态,请稍后!");
}
@Override
public void applyLoans(int loansLimit) {
System.out.println("等待放款状态,请稍后!");
}
//模拟放款,在实际项目中放款操作应该由另一方来完成的,此处执行模拟
//放款成功后,修改当前状态为支持后续操作的"还款状态"
@Override
public void grant() {
activityContext.convertState(activityContext.getRepaymentState());
System.out.println("放款成功");
}
@Override
public void repayment(int repaymentLimit) {
System.out.println("等待放款状态,请稍后!");
}
}
还款状态,所有贷款偿还完毕后,状态修改为"申请额度状态",还款状态中,如有还有未使用额度,可以再次发起贷款申请,查看申请方法,通过持有的上下文环境对象,设置当前状态为申请贷款状态,接着通过上线文环境对象调用申请方法,此时执行的是申请状态中的申请方法
class RepaymentState extends State {
public RepaymentState(ActivityContext activityContext) {
super(activityContext);
}
//当还款还款状态中发起试算额度申请时,提示不能试算
@Override
public void applyLimit(int limit) {
if(0 < activityContext.getDebt() || 0 < activityContext.getLimit()){
System.out.println("贷款未偿还完毕或还有剩余额度,不能发起额度试算");
}
}
//申请贷款方法,还款状态中,若还有剩余额度,允许客户发起第二次贷款申请
@Override
public void applyLoans(int loansLimit) {
//获取剩余额度
int limit = activityContext.getLimit();
//获取欠款金额
int debt = activityContext.getDebt();
//试算额度与申请贷款额度相差的绝对值
int val = Math.abs(limit-loansLimit);
if(0 == limit && 0 < debt){
System.out.println("额度为0,并且贷款未偿还完毕,不能再次申请!");
return;
}
//还有剩余额度
if(0 < limit){
//通过持有的上下文对象,修改当前状态为申请贷款状态
activityContext.setState(activityContext.getApplyLoansState());
//此时状态是"申请贷款状态",调用申请贷款方法,实际执行的是"申请贷款"中的
activityContext.applyLoans(loansLimit);
}
}
@Override
public void grant() {
System.out.println("没有贷款申请,请提交贷款申请!");
}
//还款方法,当用户全部还款完毕,还有剩余额度时,修改
//当前状态为"申请贷款状态",当全部还款完毕,并且额度用完
//时,修改状态为"试算额度",状态
@Override
public void repayment(int repaymentLimit) {
//获取实际欠款
int debt = activityContext.getDebt();
//获取额度
int limit = activityContext.getLimit();
if(debt < repaymentLimit){
System.out.println("还款失败,还款金额超过实际欠款金额!");
return;
}
activityContext.setDebt(debt-repaymentLimit);
System.out.println("本次还款:"+repaymentLimit +",剩余:"+activityContext.getDebt());
//还款完毕,并且额度用尽,修改状态为"试算额度"
if(0 == activityContext.getDebt() && 0 == limit){
activityContext.convertState(activityContext.getApplyLimitState());
System.out.println("所有贷款偿还完毕,额度使用完毕,请重新试算额度");
}
//还款完毕,还有试算额度,修改状态为"申请贷款"
if(0 == activityContext.getDebt() && 0 < limit){
this.activityContext.convertState(activityContext.getApplyLoansState());
System.out.println("所有贷款偿还完毕,额度未使用完毕,再次贷款直接申请即可");
}
}
}
- 调用测试
public static void main(String[] args) {
//创建上下文环境对象(在创建时通过构造器,初始化状态为"试算额度")
ActivityContext activityContext = new ActivityContext();
//调用试算额度方法,生成额度 10,执行方法后会将状态转换为"申请贷款状态"
activityContext.applyLimit(10);
//调用申请贷款方法,当前状态是"申请贷款",贷款执行完毕后会将状态转换为"等待放款"
activityContext.applyLoans(9);
//调用放款方法
activityContext.grant();
//调用还款方法,此时剩余4万贷款未还,并且剩余额度1万,不会修改状态
activityContext.repayment(5);
//再次试算额度(提示"贷款未偿还完毕或额度未使用完毕不能发起额度试算")
activityContext.applyLimit(10);
//还有剩余额度,再次发起申请1万,
activityContext.applyLoans(1);
//再次放款
activityContext.grant();
//还款
activityContext.repayment(1);
activityContext.applyLoans(5);
activityContext.repayment(4);
activityContext.applyLoans(10);
}
业务与设计模式落地示例
- 状态设计模式适用于有多个状态的对象,这些状态对应于对象的不同行为,每个状态都可以根据当前对象的状态来决定是否执行自己的行为
- 根据订单状态完成指定业务功能使用状态模式实现
- 定义订单对象,其中包括订单号、金额、状态等属性
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Order {
private String orderNo;
private BigDecimal amount;
private OrderStatus status;
}
- 定义订单状态枚举类型,其中包括待支付、已支付、已发货、已完成和已取消等各种状态
public enum OrderStatus {
WAIT_PAYMENT, // 待支付
PAID, // 已支付
SHIPPED, // 已发货
COMPLETED, // 已完成
CANCELED // 已取消
}
- 定义订单状态接口,提供默认方法
public interface OrderState {
default void pay(Order order) {
throw new RuntimeException("订单状态错误");
}
default void ship(Order order) {
throw new RuntimeException("订单状态错误");
}
default void complete(Order order) {
throw new RuntimeException("订单状态错误");
}
default void cancel(Order order) {
throw new RuntimeException("订单状态错误");
}
}
- 定义具体状态执行动作的实现类
public class WaitPaymentState implements OrderState {
@Override
public void pay(Order order) {
System.out.println("订单已支付,准备发货");
order.setStatus(OrderStatus.PAID);
}
}
public class PaidState implements OrderState {
@Override
public void pay(Order order) {
System.out.println("订单已支付");
}
@Override
public void ship(Order order) {
System.out.println("订单已发货");
order.setStatus(OrderStatus.SHIPPED);
}
@Override
public void complete(Order order) {
System.out.println("订单已完成");
order.setStatus(OrderStatus.COMPLETED);
}
@Override
public void cancel(Order order) {
System.out.println("订单已取消");
order.setStatus(OrderStatus.CANCELED);
}
}
public class ShippedState implements OrderState {
//...
}
public class CompletedState implements OrderState {
//...
}
public class CanceledState implements OrderState {
//...
}
- 定义OrderController控制器,用于接收来自客户端的订单状态变更请求,并根据当前订单状态调用相应的状态对象处理请求
@RestController
@RequestMapping("/order")
@AllArgsConstructor
public class OrderController {
private final Map<OrderStatus, OrderState> states;
@PostMapping("pay")
public String pay(@RequestBody Order order) {
OrderState state = getState(order);
state.pay(order);
return "success";
}
@PostMapping("ship")
public String ship(@RequestBody Order order) {
OrderState state = getState(order);
state.ship(order);
return "success";
}
@PostMapping("complete")
public String complete(@RequestBody Order order) {
OrderState state = getState(order);
state.complete(order);
return "success";
}
@PostMapping("cancel")
public String cancel(@RequestBody Order order) {
OrderState state = getState(order);
state.cancel(order);
return "success";
}
private OrderState getState(Order order) {
return states.get(order.getStatus());
}
}