zoukankan      html  css  js  c++  java
  • 21.java设计模式之状态模式

    基本需求

    • 需求一

      • 编写一个抽奖活动
        • 每次抽奖需要扣除50,中奖概率为10%
        • 奖品数量固定,抽完就不能抽奖
        • 活动有四个状态:不能抽奖、 可以抽奖、发放奖品、奖品领完
    • 需求二

      • 编写一个借贷平台
        • 初始是订单生成状态,审核失败进入完成状态,审核通过进入已审核状态
        • 已审核状态进行定价发布,进入已发布状态
        • 已发布状态有人接单进入待付款状态,无人接单进入完成状态
        • 待付款状态付款成功进入已付款状态,付款失败进入完成状态
        • 已付款状态反馈之后进入完成状态

    基本介绍

    • 在状态模式(State)中,类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的context对象

    • 状态模式主要用来解决对象在多种状态转换时,需要对外输出不同行为的问题,状态和行为是一一对应的,状态之间可以相互转换

    • 当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类

    • 对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为

    • UML类图(原理)

      • 说明

        • Context为环境角色,用于维护State实例,这个实例用于定义当前的状态
        • State是抽象状态的角色,定义一个接口封装与Context的一个特点接口相关行为
        • ConcreteState是具体的状态实现类,每个子类实现一个与Context的一个状态相关的行为
    • UML类图(抽奖案例)

      • 说明

        • State作为状态接口有四个实现类
        • RaffleActivity作为Context类,其中的state属性聚合了State接口,作为了RaffleActivity当前状态
        • RaffleActivity与State接口的四个实现类均为一对一的关联关系
      • 代码实现

        • // 此为状态接口,定义一系列行为
          public interface State {
          
             // 扣钱
             void deductMoney();
          
             // 抽奖
             boolean raffle();
          
             // 发放奖品
             void dispensePrize();
          }
          
          // 实现类一 不能抽奖状态
          class NoRaffleState implements State {
          
             private RaffleActivity raffleActivity;
          
             public NoRaffleState(RaffleActivity raffleActivity) {
                this.raffleActivity = raffleActivity;
             }
          
             @Override
             public void deductMoney() {
                // 判断是否还有奖品 如果没有奖品了 直接进入奖品发放完毕状态
                if (this.raffleActivity.getCount() <= 0) {
                    System.out.println("奖品发放完了,活动已结束");
                    this.raffleActivity.setState(this.raffleActivity.getDispensePrizeOutState());
                    // 直接退出程序
                    System.exit(0);
                }
                // 扣除金钱成功 将活动的状态修改为抽奖状态
                System.out.println("扣除50元成功,您可以抽奖了!");
                this.raffleActivity.setState(this.raffleActivity.getCanRaffleState());
             }
          
             @Override
             public boolean raffle() {
                System.out.println("未付款,不能抽奖,请先付款");
                return false;
             }
          
             @Override
             public void dispensePrize() {
                System.out.println("还未抽奖,不能发放奖品");
             }
          }
          
          // 实现类二 可以抽奖状态
          class CanRaffleState implements State {
          
             private RaffleActivity raffleActivity;
          
             public CanRaffleState(RaffleActivity raffleActivity) {
                this.raffleActivity = raffleActivity;
             }
          
             @Override
             public void deductMoney() {
                System.out.println("金钱已经扣除");
             }
          
             @Override
             public boolean raffle() {
                System.out.println("正在抽奖...请稍等!");
                // 有百分之十的概率中奖
                Random random = new Random();
                int i = random.nextInt(10);
                if (0 == i) {
                    // 中奖 将状态修改发放奖品状态
                    System.out.println("恭喜你中奖了,发放奖品中...");
                    this.raffleActivity.setState(this.raffleActivity.getDispensePrizeState());
                    return true;
                } else {
                    // 未中奖 将状态修改成不能抽奖状态
                    System.out.println("很遗憾,未能中奖,再来一次!");
                    this.raffleActivity.setState(this.raffleActivity.getNoRaffleState());
                    return false;
                }
             }
          
             @Override
             public void dispensePrize() {
                System.out.println("还未抽奖,不能发放奖品");
             }
          }
          
          // 实现类三 发放奖品状态
          class DispensePrizeState implements State {
          
             private RaffleActivity raffleActivity;
          
             public DispensePrizeState(RaffleActivity raffleActivity) {
                this.raffleActivity = raffleActivity;
             }
          
             @Override
             public void deductMoney() {
                System.out.println("发放奖品中,不能扣除金钱");
             }
          
             @Override
             public boolean raffle() {
                System.out.println("发放奖品中,不能抽奖");
                return false;
             }
          
             @Override
             public void dispensePrize() {
                // 如果有奖品 就发放奖品 发放之后奖品数量减一 之后进入不能抽奖状态
                System.out.println("奖品已发放...请接收!");
                this.raffleActivity.setCount(this.raffleActivity.getCount() - 1);
                this.raffleActivity.setState(this.raffleActivity.getNoRaffleState());
             }
          }
          
          // 实现类四 奖品发放完毕状态
          class DispensePrizeOutState implements State {
          
             private RaffleActivity raffleActivity;
          
             public DispensePrizeOutState(RaffleActivity raffleActivity) {
                this.raffleActivity = raffleActivity;
             }
          
             @Override
             public void deductMoney() {
                System.out.println("奖品发放完了,活动已结束");
             }
          
             @Override
             public boolean raffle() {
                System.out.println("奖品发放完了,活动已结束");
                return false;
             }
          
             @Override
             public void dispensePrize() {
                System.out.println("奖品发送完了,活动已结束");
             }
          }
          
        • // 抽奖活动类 此处作为Context类
          @Data
             public class RaffleActivity {
          
             // 此处使用状态接口 设置了不同的状态实现类 同一个类就会做出不同的行为
             private State state;
          
             // 本次活动的奖品数量 数量完毕后 就结束活动
             private Integer count;
          
             // 组合所有的状态 便于使用
             private State noRaffleState = new NoRaffleState(this);
          
             private State canRaffleState = new CanRaffleState(this);
          
             private State dispensePrizeState = new DispensePrizeState(this);
          
             private State dispensePrizeOutState = new DispensePrizeOutState(this);
          
             public RaffleActivity(Integer count) {
                this.count = count;
                // 活动初始状态为不能抽奖状态
                this.state = this.noRaffleState;
             }
          
             // 扣钱
             public void deductMoney() {
                this.state.deductMoney();
             }
          
             // 抽奖
             public void raffle() {
                if (this.state.raffle()) {
                    // 中奖发送奖品
                    this.state.dispensePrize();
                }
             }
          }
          
          
        • public class Client {
             public static void main(String[] args) {
                // 创建有n个奖品的抽奖活动
                RaffleActivity raffleActivity = new RaffleActivity(3);
                for (int i = 0; i < 100; i++) {
                    System.out.println("--------第" + (i + 1) + "次抽奖----------");
                    // 扣金钱 抽奖
                    raffleActivity.deductMoney();
                    raffleActivity.raffle();
                }
             }
          }
          
    • UML类图(借贷案例)

      • 说明

        • State是状态接口
        • AbstractState是抽象实现类,对接口的方法全部进行了实现,只不过每个方法均是抛出了异常
        • 其余的六个xxxState为具体的状态类,继承AbstractState,对其中的方法进行有选择的重写、
        • Context为环境类,此处代表订单,聚合了状态接口
        • StateEnum为状态枚举类,方便使用枚举
    • 代码实现

      • // 状态接口
        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();
        }
        
        // 抽象类 对接口进行空实现,子类可有选择的覆盖其中方法
        public abstract class AbstractState implements State{
        
           protected static final RuntimeException EXCEPTION = new RuntimeException("操作流程不允许");
        
           @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;
           }
        }
        
        
      • public class AllState {
        
        }
        
        // 子类一 订单生成状态
        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 ReviewState extends AbstractState {
        
           // 定价发布 进入已发布状态
           @Override
           public void makePriceEvent(Context context) {
              context.setState(new PublishState());
           }
        
           @Override
           public String getCurrentState() {
              return StateEnum.REVIEWED.getValue();
           }
        }
        
        // 子类三 已发布状态
        class PublishState extends AbstractState {
        
           // 有人接单 进入待付款状态
           @Override
           public void acceptOrderEvent(Context context) {
              context.setState(new NotPayState());
           }
        
           // 无人接单 进入已完成状态
           @Override
           public void notPeopleAcceptEvent(Context context) {
              context.setState(new FeedBackState());
           }
        
           @Override
           public String getCurrentState() {
              return StateEnum.PUBLISHED.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 FeedBackState extends AbstractState {
        
           @Override
           public String getCurrentState() {
              return StateEnum.FEED_BACKED.getValue();
           }
        }
        
      • // 状态枚举类 用于获取状态
        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;}
        }
        
        
      • // 上下文环境类 此处就是订单
        public class Context extends AbstractState{
        
           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();
           }
        }
        
        
      • public class Client {
           public static void main(String[] args) {
              Context context = new Context();
              // 订单生成
              context.setState(new GenerateState());
              context.getCurrentState();
              // 审核成功
              context.checkEvent(context);
              // 发布
              context.makePriceEvent(context);
              // 接单
              context.acceptOrderEvent(context);
              //付款
              context.payOrderEvent(context);
              // 反馈
              context.feedBackEvent(context);
           }
        }
        
        

    注意事项

    • 代码有很强的可读性,状态模式将每个状态的行为封装到对应的一个类中
    • 方便维护,将容易产生问题的if-else语句删除了,如果把每个状态的行为都放到一个类中,每次调用方法时都要判断当前是什么状态,不但会产出很多 if-else 语句,而且容易出错
    • 符合“开闭原则”,容易增删状态
    • 会产生很多类,每个状态都要一个对应的类,当状态过多时会产生很多类,加大维护难度
    • 当一个事件或者对象有很多种状态,状态之间会相互转换,对不同的状态要求有不同的行为的时候,可以考虑使用状态模式
  • 相关阅读:
    window.history 和 DWZ 框架
    Ztree 随笔记
    eval的对于验证数学公式的用处
    lodop打印控件一点记录
    font和lineheight冲突。
    Windows CMD命令大全
    centos 下安装pip pip3
    Linux访问windows共享文件夹
    数据库主从和读写分离的配置和使用方法
    centos7 nginx+php7yum安装
  • 原文地址:https://www.cnblogs.com/xiaokantianse/p/14384015.html
Copyright © 2011-2022 走看看