状态模式

状态模式 State 概述

基本介绍

  • 状态模式主要用来解决对象在多种状态之间转换时,不同状态,行为不同的问题,当一个对象的内在状态发生改变时,该对象的行为也会发送相应的改变,在传统模式下通常使用一个字段来标识一个类的状态,然后通过逻辑代码去判断获取相应的状态,执行相应的操作,假设后续更改添加状态需要修改逻辑代码
  • 状态模式的适用场景: 一个对象存在多个状态,且状态之间可以相互转换,不同状态行为不同时
  • 状态模式的优点: 每个状态对应一个类,不同状态之间相互隔离,把状态的转换逻辑放入不同的状态类中,减少相互之间的依赖,增加新的状态非常简单,利于扩展
  • 缺点: 状态过多时,需要创建多个状态类

状态模式角色分析

  • 上下文环境 Context:与客户端的交互层,包含执行功能所需要的所有数据,持有所有状态对象,与当前状态对象,提供执行功能的方法,功能的执行实际是通过持有的当前状态对象来调用执行的
  • 抽象状态 State:持有上下文环境对象,创建具体状态对象,需要初始化赋值持有的上下文环境对象,这个上下文环境对象代表的是当前,提供执行功能的抽象方法
  • 具体状态 ConcreteState :具体状态一般存在多个,实现抽象状态,根据不同状态执行功能的不同,重写功能方法,在状态发生改变时通过持有的上下文环境对象,获取上下文中持有的对应的状态,赋值给上下文环境对象中的当前状态即可,当前状态就发生了改变,后续客户端通过上下文环境对象执行功能时,获取当前状态也就是改变后的状态执行对应的功能

自己理解的状态模式
上下文环境对象与状态对象相互持有对方,上下文中通过持有的当前状态对象执行功能方法,状态对象中,通过持有上下文对象,修改当前状态

示例

案例

客户贷款,贷款首先要试算额度,当有额度时,可以申请贷款,申请贷款额度不可以超过试算额度,等待放款,还款,假设客户试算额度为20万,实际贷款为10万,客户可以不试算额度,继续发起贷款申请,但客户额度使用完毕,若想再次贷款,需要重新试算额度,重新申请,重新试算额度需要偿清前面的所有贷款

分析案例(不是太严谨,只关注设计模式)

  • 功能: 额度试算, 申请贷款, 放款, 还款
  • 涉及到两个数据,试算额度,贷款额度这就是上
  • 状态: 试算额度状态,可以发起额度试算,获取额度,申请贷款状态,可以发起贷款申请,等待放款状态,还款状态,可以还款,可以再次发起贷款申请

代码

  1. 根据公共数据,所有的状态,执行的功能,创建与客户端的交互层上下文环境对象
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;
    }
}
  1. 根据功能,创建抽象状态父接口,提供抽象功能方法,在具体状态中,状态不同功能不同进行具体实现
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);

}
  1. 创建具体状态
    试算额度状态,额度生成后通过持有的上下文环境对象,修改状态为"申请贷款状态"
//试算额度状态(也可看为初始化状态)
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("所有贷款偿还完毕,额度未使用完毕,再次贷款直接申请即可");
        }
    }
}
  1. 调用测试
	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);
    }

业务与设计模式落地示例

  1. 状态设计模式适用于有多个状态的对象,这些状态对应于对象的不同行为,每个状态都可以根据当前对象的状态来决定是否执行自己的行为
  2. 根据订单状态完成指定业务功能使用状态模式实现
  3. 定义订单对象,其中包括订单号、金额、状态等属性
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Order {
    private String orderNo;
    private BigDecimal amount;
    private OrderStatus status;
}
  1. 定义订单状态枚举类型,其中包括待支付、已支付、已发货、已完成和已取消等各种状态
public enum OrderStatus {
    WAIT_PAYMENT, // 待支付
    PAID, // 已支付
    SHIPPED, // 已发货
    COMPLETED, // 已完成
    CANCELED // 已取消
}
  1. 定义订单状态接口,提供默认方法
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("订单状态错误");
    }
}
  1. 定义具体状态执行动作的实现类
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 {
   //...
}
  1. 定义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());
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值