【设计模式——学习笔记】23种设计模式——状态模式State(原理讲解+应用场景介绍+案例介绍+Java代码实现)

文章目录

  • 案例引入
  • 介绍
    • 基本介绍
    • 登场角色
    • 应用场景
  • 案例实现
    • 案例一
      • 类图
      • 实现
    • 案例二:借贷平台源码剖析
      • 传统方式实现分析
      • 状态修改流程
      • 类图
      • 实现
    • 案例三:金库警报系统
      • 系统的运行逻辑
      • 伪代码
        • 传统实现方式
        • 使用状态模式
      • 类图
      • 实现
      • 分析
      • 问题
        • 问题一
        • 问题二
  • 总结
  • 文章说明

案例引入

请编写程序完成APP抽奖活动具体要求如下:

  • 假如每参加一次这个活动要扣除用户50积分,中奖概率是10%
  • 奖品数量固定,抽完就不能抽奖
  • 活动有四个状态: 可以抽奖、不能抽奖、发放奖品和奖品领完,活动的四个状态转换关系图如下

在这里插入图片描述

一开始的状态为“不能抽奖”,当扣除50积分成功之后,状态就变成了“可以抽奖”状态

介绍

基本介绍

  • 状态模式: 它主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题。状态和行为是一一对应的(如果处于A状态,就拥有A状态所拥有的行为和操作),状态之间可以相互转换
  • 当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是变成了另外一个类的对象
  • 在状态模式中,使用类来表示状态,可以通过切换类来改变对象的状态,当需要增加新的类时,也只需要增加新的类即可

登场角色

在这里插入图片描述

  • Context(上下文):用于维护State实例, 根据state的不同,实例对应的ConcreteState类也不同,这样子State对象的方法也不同
  • State(状态):抽象状态角色,定义多个接口
  • ConcreteState(具体状态):是具体状态角色,根据自身的状态来实现State接口的方法

应用场景

  • 当一个事件或者对象有很多种状态,状态之间会相互转换,对不同的状态要求有不同的行为的时候,可以考虑使用状态模式

案例实现

案例一

类图

在这里插入图片描述

Activity类含有所有的状态对象,各个状态子类也含有Activity对象

实现

【抽象状态类:State】

package com.atguigu.state;/*** 状态抽象类** @author Administrator*/
public abstract class State {/*** 扣除积分 - 50*/public abstract void deductMoney();/*** 是否抽中奖品** @return*/public abstract boolean raffle();/*** 发放奖品*/public abstract void dispensePrize();}

不能抽奖状态

package com.atguigu.state;/*** 不能抽奖状态* @author Administrator**/
public class NoRaffleState extends State {/*** 初始化时传入活动引用,扣除积分后改变其状态*/RaffleActivity activity;public NoRaffleState(RaffleActivity activity) {this.activity = activity;}/*** 当前状态可以扣积分,扣除后,将状态设置成可以抽奖状态*/@Overridepublic void deductMoney() {System.out.println("扣除50积分成功,您可以抽奖了");activity.setState(activity.getCanRaffleState());}/*** 当前状态不能抽奖* @return*/@Overridepublic boolean raffle() {System.out.println("扣了积分才能抽奖喔!");return false;}/*** 当前状态不能发奖品*/@Overridepublic void dispensePrize() {System.out.println("不能发放奖品");}
} 

【可以抽奖的状态】

package com.atguigu.state;import java.util.Random;/*** 可以抽奖的状态** @author Administrator*/
public class CanRaffleState extends State {RaffleActivity activity;public CanRaffleState(RaffleActivity activity) {this.activity = activity;}/*** 已经扣除了积分,不能再扣*/@Overridepublic void deductMoney() {System.out.println("已经扣取过了积分");}/*** 可以抽奖, 抽完奖后,根据实际情况,改成新的状态** @return*/@Overridepublic boolean raffle() {System.out.println("正在抽奖,请稍等!");Random r = new Random();int num = r.nextInt(10);// 10%中奖机会if (num == 0) {// 改变活动状态为发放奖品 contextactivity.setState(activity.getDispenseState());return true;} else {System.out.println("很遗憾没有抽中奖品!");// 改变状态为不能抽奖activity.setState(activity.getNoRafflleState());return false;}}/*** 不能发放奖品*/@Overridepublic void dispensePrize() {System.out.println("没中奖,不能发放奖品");}
}

【发放奖品的状态】

package com.atguigu.state;/*** 发放奖品的状态** @author Administrator*/
public class DispenseState extends State {/*** 初始化时传入活动引用,发放奖品后改变其状态*/RaffleActivity activity;public DispenseState(RaffleActivity activity) {this.activity = activity;}@Overridepublic void deductMoney() {System.out.println("不能扣除积分");}@Overridepublic boolean raffle() {System.out.println("不能抽奖");return false;}//发放奖品@Overridepublic 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.atguigu.state;/*** 奖品发放完毕状态* 说明,当我们activity 改变成 DispenseOutState, 抽奖活动结束** @author Administrator*/
public class DispenseOutState extends State {/*** 初始化时传入活动引用*/RaffleActivity activity;public DispenseOutState(RaffleActivity activity) {this.activity = activity;}@Overridepublic void deductMoney() {System.out.println("奖品发送完了,请下次再参加");}@Overridepublic boolean raffle() {System.out.println("奖品发送完了,请下次再参加");return false;}@Overridepublic void dispensePrize() {System.out.println("奖品发送完了,请下次再参加");}
}

【运行】

--------第1次抽奖----------
扣除50积分成功,您可以抽奖了
正在抽奖,请稍等!
很遗憾没有抽中奖品!
--------第2次抽奖----------
扣除50积分成功,您可以抽奖了
正在抽奖,请稍等!
很遗憾没有抽中奖品!
--------第3次抽奖----------
扣除50积分成功,您可以抽奖了
正在抽奖,请稍等!
很遗憾没有抽中奖品!
--------第4次抽奖----------
扣除50积分成功,您可以抽奖了
正在抽奖,请稍等!
很遗憾没有抽中奖品!
--------第5次抽奖----------
扣除50积分成功,您可以抽奖了
正在抽奖,请稍等!
很遗憾没有抽中奖品!
--------第6次抽奖----------
扣除50积分成功,您可以抽奖了
正在抽奖,请稍等!
很遗憾没有抽中奖品!
--------第7次抽奖----------
扣除50积分成功,您可以抽奖了
正在抽奖,请稍等!
恭喜中奖了
--------第8次抽奖----------
扣除50积分成功,您可以抽奖了
正在抽奖,请稍等!
很遗憾没有抽中奖品!
--------第9次抽奖----------
扣除50积分成功,您可以抽奖了
正在抽奖,请稍等!
很遗憾,奖品发送完了
--------第10次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第11次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第12次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第13次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第14次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第15次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第16次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第17次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第18次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第19次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第20次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第21次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第22次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第23次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第24次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第25次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第26次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第27次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第28次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第29次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第30次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加Process finished with exit code 0

案例二:借贷平台源码剖析

传统方式实现分析

通过if/else判断订单的状态,从而实现不同的逻辑

【分析】

这类代码难以应对变化,在添加一种状态时,我们需要手动添加if/else,在添加一种功能时,要对所有的状态进行判断。因此代码会变得越来越臃肿,并且一旦没有处理某个状态便会发生极其严重的BUG,难以维护

【改进】

借贷平台的订单,有审核-发布-抢单等等步骤,随着操作的不同,会改变订单的状态,项目中的这个模块实现就会使用到状态模式

状态修改流程

在这里插入图片描述

类图

在这里插入图片描述

实现

【状态枚举类:StateEnum】

package com.atguigu.state.money;/*** 状态枚举类* @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;}
}

【状态接口:State】

package com.atguigu.state.money;/*** 状态接口* @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.atguigu.state.money;/*** 抽象类,默认实现了 State 接口的所有方法* 该类的所有方法,其子类(具体的状态类),可以有选择的进行重写*/
public abstract class AbstractState implements State {protected static final RuntimeException EXCEPTION = new RuntimeException("操作流程不允许");@Overridepublic void checkEvent(Context context) {throw EXCEPTION;}@Overridepublic void checkFailEvent(Context context) {throw EXCEPTION;}@Overridepublic void makePriceEvent(Context context) {throw EXCEPTION;}@Overridepublic void acceptOrderEvent(Context context) {throw EXCEPTION;}@Overridepublic void notPeopleAcceptEvent(Context context) {throw EXCEPTION;}@Overridepublic void payOrderEvent(Context context) {throw EXCEPTION;}@Overridepublic void orderFailureEvent(Context context) {throw EXCEPTION;}@Overridepublic void feedBackEvent(Context context) {throw EXCEPTION;}
}

【所有的具体状态类都在这个文件里面】

package com.atguigu.state.money;/*** 反馈状态*/
class FeedBackState extends AbstractState {@Overridepublic String getCurrentState() {return StateEnum.FEED_BACKED.getValue();}
}/*** 通用状态*/
class GenerateState extends AbstractState {@Overridepublic void checkEvent(Context context) {context.setState(new ReviewState());}@Overridepublic void checkFailEvent(Context context) {context.setState(new FeedBackState());}@Overridepublic String getCurrentState() {return StateEnum.GENERATE.getValue();}
}/*** 未支付状态*/
class NotPayState extends AbstractState {@Overridepublic void payOrderEvent(Context context) {context.setState(new PaidState());}@Overridepublic void feedBackEvent(Context context) {context.setState(new FeedBackState());}@Overridepublic String getCurrentState() {return StateEnum.NOT_PAY.getValue();}
}/*** 已支付状态*/
class PaidState extends AbstractState {@Overridepublic void feedBackEvent(Context context) {context.setState(new FeedBackState());}@Overridepublic String getCurrentState() {return StateEnum.PAID.getValue();}
}/*** 发布状态*/
class PublishState extends AbstractState {@Overridepublic void acceptOrderEvent(Context context) {//接受订单成功,把当前状态设置为NotPayState//至于实际上应该变成哪个状态,由流程图来决定context.setState(new NotPayState());}@Overridepublic void notPeopleAcceptEvent(Context context) {context.setState(new FeedBackState());}@Overridepublic String getCurrentState() {return StateEnum.PUBLISHED.getValue();}
}/*** 回顾状态*/
class ReviewState extends AbstractState {@Overridepublic void makePriceEvent(Context context) {context.setState(new PublishState());}@Overridepublic String getCurrentState() {return StateEnum.REVIEWED.getValue();}}

【环境上下文】

package com.atguigu.state.money;/*** 环境上下文*/
public class Context extends AbstractState{/*** 当前的状态 state, 根据我们的业务流程处理,不停的变化*/private State state;@Overridepublic void checkEvent(Context context) {state.checkEvent(this);getCurrentState();}@Overridepublic void checkFailEvent(Context context) {state.checkFailEvent(this);getCurrentState();}@Overridepublic void makePriceEvent(Context context) {state.makePriceEvent(this);getCurrentState();}@Overridepublic void acceptOrderEvent(Context context) {state.acceptOrderEvent(this);getCurrentState();}@Overridepublic void notPeopleAcceptEvent(Context context) {state.notPeopleAcceptEvent(this);getCurrentState();}@Overridepublic void payOrderEvent(Context context) {state.payOrderEvent(this);getCurrentState();}@Overridepublic void orderFailureEvent(Context context) {state.orderFailureEvent(this);getCurrentState();}@Overridepublic void feedBackEvent(Context context) {state.feedBackEvent(this);getCurrentState();}public State getState() {return state;}public void setState(State state) {this.state = state;}@Overridepublic String getCurrentState() {System.out.println("当前状态 : " + state.getCurrentState());return state.getCurrentState();}
}

【主类】

package com.atguigu.state.money;/*** 测试类*/
public class ClientTest {public static void main(String[] args) {//创建context 对象Context context = new Context();//将当前状态设置为 PublishStatecontext.setState(new PublishState());System.out.println(context.getCurrentState());//        //publish --> not paycontext.acceptOrderEvent(context);
//        //not pay --> paidcontext.payOrderEvent(context);
//        // 失败, 检测失败时,会抛出异常
//        try {
//         context.checkFailEvent(context);
//         System.out.println("流程正常..");
//    } catch (Exception e) {
//       System.out.println(e.getMessage());
//    }}}

【运行】

当前状态 : PUBLISHED
PUBLISHED
当前状态 : NOT_PAY
当前状态 : PAIDProcess finished with exit code 0

案例三:金库警报系统

系统的运行逻辑

在这里插入图片描述

伪代码

传统实现方式

在这里插入图片描述

使用状态模式

在这里插入图片描述

类图

在这里插入图片描述

实现

【状态接口】

package com.atguigu.state.Sample;public interface State {/*** 设置时间** @param context* @param hour*/public abstract void doClock(Context context, int hour);/*** 使用金库** @param context*/public abstract void doUse(Context context);/*** 按下警铃** @param context*/public abstract void doAlarm(Context context);/*** 正常通话** @param context*/public abstract void doPhone(Context context);
}

【白天状态】

package com.atguigu.state.Sample;
/*** 表示白天的状态*/
public class DayState implements State {/*** 每个状态都是一个类,如果每次改变状态都需要生成一个新的实例的话,比较浪费内存和时间,所以在这里使用单例模式*/private static DayState singleton = new DayState();/*** 构造函数的可见性是private*/private DayState() {}/*** 获取唯一实例* @return*/public static State getInstance() {return singleton;}/*** 设置时间* @param context* @param hour*/public void doClock(Context context, int hour) {if (hour < 9 || 17 <= hour) {// 如果时间是晚上,切换到夜间状态context.changeState(NightState.getInstance());}}/*** 使用金库* @param context*/public void doUse(Context context) {context.recordLog("使用金库(白天)");}/*** 按下警铃* @param context*/public void doAlarm(Context context) {context.callSecurityCenter("按下警铃(白天)");}/*** 正常通话* @param context*/public void doPhone(Context context) {context.callSecurityCenter("正常通话(白天)");}/*** 显示表示类的文字* @return*/public String toString() {return "[白天]";}
}

【夜间状态】

package com.atguigu.state.Sample;public class NightState implements State {private static NightState singleton = new NightState();/*** 构造函数的可见性是private*/private NightState() {}/*** 获取唯一实例* @return*/public static State getInstance() {                 return singleton;}/*** 设置时间* @param context* @param hour*/public void doClock(Context context, int hour) {    if (9 <= hour && hour < 17) {context.changeState(DayState.getInstance());}}/*** 使用金库* @param context*/public void doUse(Context context) {                context.callSecurityCenter("紧急:晚上使用金库!");}/*** 按下警铃* @param context*/public void doAlarm(Context context) {              context.callSecurityCenter("按下警铃(晚上)");}/*** 正常通话* @param context*/public void doPhone(Context context) {              context.recordLog("晚上的通话录音");}/*** 显示表示类的文字* @return*/public String toString() {                          return "[晚上]";}
}

【Context接口】

  • 负责管理状态和联系警报中心
package com.atguigu.state.Sample;public interface Context {/*** 设置时间** @param hour*/public abstract void setClock(int hour);/*** 改变状态** @param state*/public abstract void changeState(State state);/*** 联系警报中心** @param msg*/public abstract void callSecurityCenter(String msg);/*** 在警报中心留下记录** @param msg*/public abstract void recordLog(String msg);
}

【Context角色:SafeFrame】

在这个实例程序中,Context角色的作用被Context接口和SafeFrame类分担了。Context接口定义了供外部调用者使用状态模式的接口,而SafeFrame类持有表示当前状态的ConcreteState角色

package com.atguigu.state.Sample;import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;public class SafeFrame extends Frame implements ActionListener, Context {/*** 显示当前时间*/private TextField textClock = new TextField(60);/*** 显示警报中心的记录*/private TextArea textScreen = new TextArea(10, 60);/*** 金库使用按钮*/private Button buttonUse = new Button("使用金库");/*** 按下警铃按钮*/private Button buttonAlarm = new Button("按下警铃");/*** 正常通话按钮*/private Button buttonPhone = new Button("正常通话");/*** 结束按钮*/private Button buttonExit = new Button("结束");/*** 金库当前的状态*/private State state = DayState.getInstance();/*** 构造函数** @param title*/public SafeFrame(String title) {super(title);setBackground(Color.lightGray);setLayout(new BorderLayout());//  配置textClockadd(textClock, BorderLayout.NORTH);textClock.setEditable(false);// 配置textScreenadd(textScreen, BorderLayout.CENTER);textScreen.setEditable(false);// 为界面添加按钮Panel panel = new Panel();panel.add(buttonUse);panel.add(buttonAlarm);panel.add(buttonPhone);panel.add(buttonExit);// 配置界面add(panel, BorderLayout.SOUTH);// 显示pack();show();// 设置监听器buttonUse.addActionListener(this);buttonAlarm.addActionListener(this);buttonPhone.addActionListener(this);buttonExit.addActionListener(this);}/*** 按钮被按下后,该方法会被调用** @param e*/public void actionPerformed(ActionEvent e) {System.out.println(e.toString());if (e.getSource() == buttonUse) {// 金库使用按钮,并不需要去判断状态,直接调用即可state.doUse(this);} else if (e.getSource() == buttonAlarm) {// 按下警铃按钮state.doAlarm(this);} else if (e.getSource() == buttonPhone) {// 正常通话按钮state.doPhone(this);} else if (e.getSource() == buttonExit) {// 结束按钮System.exit(0);} else {System.out.println("?");}}/*** 设置时间** @param hour*/public void setClock(int hour) {String clockstring = "现在时间是";if (hour < 10) {clockstring += "0" + hour + ":00";} else {clockstring += hour + ":00";}System.out.println(clockstring);// 将当前时间显示在界面的上方textClock.setText(clockstring);// 进行当前状态下的处理state.doClock(this, hour);}/*** 改变状态** @param state*/public void changeState(State state) {System.out.println("从" + this.state + "状態变为了" + state + "状态。");this.state = state;}/*** 联系警报中心** @param msg*/public void callSecurityCenter(String msg) {textScreen.append("call! " + msg + "\n");}/*** 在警报中心留下记录** @param msg*/public void recordLog(String msg) {textScreen.append("record ... " + msg + "\n");}
}

【运行】

package com.atguigu.state.Sample;public class Main {public static void main(String[] args) {SafeFrame frame = new SafeFrame("State Sample");while (true) {for (int hour = 0; hour < 24; hour++) {// 设置时间frame.setClock(hour);try {Thread.sleep(1000);} catch (InterruptedException e) {}}}}
}

【运行】

在这里插入图片描述

现在时间是00:00
从[白天]状態变为了[晚上]状态。
现在时间是01:00
现在时间是02:00
java.awt.event.ActionEvent[ACTION_PERFORMED,cmd=使用金库,when=1691920919394,modifiers=] on button0
现在时间是03:00
java.awt.event.ActionEvent[ACTION_PERFORMED,cmd=按下警铃,when=1691920920040,modifiers=] on button1
java.awt.event.ActionEvent[ACTION_PERFORMED,cmd=正常通话,when=1691920920824,modifiers=] on button2
现在时间是04:00
现在时间是05:00
java.awt.event.ActionEvent[ACTION_PERFORMED,cmd=按下警铃,when=1691920922071,modifiers=] on button1
java.awt.event.ActionEvent[ACTION_PERFORMED,cmd=使用金库,when=1691920922626,modifiers=] on button0
现在时间是06:00
现在时间是07:00
现在时间是08:00
java.awt.event.ActionEvent[ACTION_PERFORMED,cmd=使用金库,when=1691920925446,modifiers=] on button0
现在时间是09:00
从[晚上]状態变为了[白天]状态。
现在时间是10:00
现在时间是11:00
现在时间是12:00
现在时间是13:00
现在时间是14:00
现在时间是15:00
Disconnected from the target VM, address: '127.0.0.1:1966', transport: 'socket'Process finished with exit code 130

分析

上面的实现方式中,由具体状态类来实际调用方法切换到另一个状态,如DayState类的doClock方法,这种方式既有优点又有缺点:

  • 优点:当我们想知道“什么时候从DayState的类变化为其他状态”,只需要查看DayState类即可
  • 缺点:每个ConcreteState角色都需要知道其他ConcreteState角色的方法,各个类之间的依赖关系较强,如果删除了一个ConcreteState类,就需要修改其他的ConcreteState类

除了这种实现方式之外,也可以将所有的状态迁移交给扮演Context角色的类来负责,这样可以提高ConcreteState角色的独立性,程序的整体结构也会更加清晰,当然,这样做需要Context角色知道所有的ConcreteState角色,可以使用中介者模式来改进

问题

问题一

问:将Context定义为抽象类而非接口,然后让Context类持有state字段这样更符合状态模式的设计思想。但是在示例程序中我们并没有这么做,而是将Context角色定义为Context接口,让SafeFrame类持有state字段,请问这是为什么呢?

答:Java中只能单一继承,所以如果将Context角色定义为类,那么由于SafeFrame类已经是Frame类的子类了,它将无法再继承Context 类。不过,如果另外编写一个Context类的子类,并将它的实例保存在SafeFrame类的字段中那么通过将处理委托给这个实例是可以实现上述问题的需求的。

问题二

请在示例程序中增加一个新的“紧急情况”状态。不论是什么时间,只要处于“紧急情况”下,就向警报中心通知紧急情况

  • 按下警铃后,系统状态变为“紧急情况”状态
  • 如果“紧急情况”下使用金库的话,会向警报中心通知紧急情况(与当时的时间无关)
  • 如果“紧急情况”下按下警铃的话,会向警报中心通知紧急情况(与当时的时间无关)
  • 如果“紧急情况”下使用电话的话,会呼叫警报中心的留言电话(与当时的时间无关)

【增加一个紧急状态类】

package com.atguigu.state.A4;public class UrgentState implements State {private static UrgentState singleton = new UrgentState();private UrgentState() {}public static State getInstance() {return singleton;}public void doClock(Context context, int hour) {// 设置时间// 在设置时间处理中什么都不做                                 }public void doUse(Context context) {// 使用金库context.callSecurityCenter("紧急:紧急时使用金库!");}public void doAlarm(Context context) {// 按下警铃context.callSecurityCenter("按下警铃(紧急时)");}public void doPhone(Context context) {// 正常通话context.callSecurityCenter("正常通话(紧急时)");}public String toString() {// 显示字符串return "[紧急时]";}
}

【修改其他状态的状态迁移方法】

package com.atguigu.state.A4;public class DayState implements State {private static DayState singleton = new DayState();private DayState() {                          }public static State getInstance() {                return singleton;}public void doClock(Context context, int hour) {    if (hour < 9 || 17 <= hour) {context.changeState(NightState.getInstance());}}public void doUse(Context context) {             context.recordLog("使用金库(白天)");}public void doAlarm(Context context) {              context.callSecurityCenter("按下警铃(白天)");// 只需要看这里就行,一旦按下紧铃,就会进入到紧急状态context.changeState(UrgentState.getInstance()); }public void doPhone(Context context) {             context.callSecurityCenter("正常通话(白天)");}public String toString() {                       return "[白天]";}
}

夜间状态也需要修改对应的状态迁移方法,和白天状态类似,这里就不再展示了

总结

【优点】

  • 代码有很强的可读性,状态模式将每个状态的行为封装到对应的一个类中方便维护
  • 将容易产生问题的if-else语句删除了,如果把每个状态的行为都放到一个类中,每次调用方法时都要判断当前是什么状态,不但会产出很多if-else语句,而且容易出错
  • 符合“开闭原则”,容易增删状态,只需要增删一个ConcreteState的类,然后修改负责状态迁移的类即可。如果使用的是传统方式,新增一个状态,就需要增加很多的判断语句
  • 使用“分而治之”的思想,将多个状态分开来,每个类只需要根据当前状态来写代码即可,不需要在执行事件之前写复杂的条件分支语句
  • 如果需要增加依赖于状态的处理方法,只需要在State接口中增加新的方法,并让所有的ConcreteState类实现这个方法,虽然修改量较大,但是开发者肯定不会忘记去实现这个方法,因为不实现,编译就会报错

【缺点】

  • 会产生很多类。每个状态都要一个对应的类,当状态过多时会产生很多类,加大维护难度

文章说明

  • 本文章为本人学习尚硅谷的学习笔记,文章中大部分内容来源于尚硅谷视频(点击学习尚硅谷相关课程),也有部分内容来自于自己的思考,发布文章是想帮助其他学习的人更方便地整理自己的笔记或者直接通过文章学习相关知识,如有侵权请联系删除,最后对尚硅谷的优质课程表示感谢。
  • 本人还同步阅读《图解设计模式》书籍(图解设计模式/(日)结城浩著;杨文轩译–北京:人民邮电出版社,2017.1),进而综合两者的内容,让知识点更加全面

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/67408.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

小米发布会:雷军成长故事与创新壮举,AI大模型技术引领未来,雷军探索之路之从创业波折到小米AI领航,成就高端化传奇!

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

【EI/SCOPUS检索】第三届计算机视觉、应用与算法国际学术会议(CVAA 2023)

第三届计算机视觉、应用与算法国际学术会议&#xff08;CVAA 2023) The 3rd International Conference on Computer Vision, Application and Algorithm 2023年第三届计算机视觉、应用与算法国际学术会议&#xff08;CVAA 2023&#xff09;主要围绕计算机视觉、计算机应用、计…

Java并发编程(六)线程池[Executor体系]

概述 在处理大量任务时,重复利用线程可以提高程序执行效率,因此线程池应运而生。 它是一种重用线程的机制,可以有效降低内存资源消耗提高响应速度。当任务到达时&#xff0c;任务可以不需要的等到线程创建就能立即执行线程池可以帮助我们更好地管理线程的生命周期和资源使用,…

算法与数据结构-哈希算法

文章目录 什么是哈希算法哈希算法的应用安全加密唯一标识数据校验散列函数负载均衡数据分片分布式存储 什么是哈希算法 哈希算法的定义和原理非常简单&#xff0c;基本上一句话就可以概括了。将任意长度的二进制值串映射为固定长度的二进制值串&#xff0c;这个映射的规则就是…

Springboot项目启动后按顺序加载自定义类 (demo)

1. 实现ApplicationRunner接口, 重写run方法 import lombok.extern.slf4j.Slf4j; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.core.annotation.Order; import org.springframewor…

如何学习嵌入式软件开发?

首先就是认知和基础阶段的学习。这部分一般都是要求学习一些行业认知类的课程&#xff0c;指导嵌入式未来的发展前景和就业趋势&#xff0c;C语言的入门&#xff0c;开发工具的使用&#xff0c;常见的命令&#xff0c;数据结构算法等内容。这一部分主要的就是要靠记忆力&#x…

华为AI战略的CANN

基于TVM的华为昇腾体系中—— 异构计算架构&#xff08;CANN&#xff09;是对标英伟达的CUDA CuDNN的核心软件层&#xff0c;向上支持多种AI框架&#xff0c;向下服务AI处理器&#xff0c;发挥承上启下的关键作用&#xff0c;是提升昇腾AI处理器计算效率的关键平台 主要包括有…

问道管理:三大股指触底反弹 AI算力方向再度崛起

周一&#xff0c;受人民币汇率动摇等要素影响&#xff0c;A股三大股指早盘深度回撤&#xff0c;沪指盘中创出1月中旬以来新低。午间休市前后&#xff0c;券商与人工智能板块相继发力&#xff0c;带动股指止跌回升&#xff0c;大盘终究以全天的相对高点报收。 截至14日收盘&…

测试人进阶技能:单元测试报告应用指南

为什么需要单元测试 从产品角度而言&#xff0c;常规的功能测试、系统测试都是站在产品局部或全局功能进行测试&#xff0c;能够很好地与用户的需要相结合&#xff0c;但是缺乏了对产品研发细节&#xff08;特别是代码细节的理解&#xff09;。 从测试人员角度而言&#xff0…

共识算法初探

共识机制的背景 加密货币都是去中心化的&#xff0c;去中心化的基础就是P2P节点众多&#xff0c;那么如何吸引用户加入网络成为节点&#xff0c;有那些激励机制&#xff1f;同时&#xff0c;开发的重点是让多个节点维护一个数据库&#xff0c;那么如何决定哪个节点写入&#x…

【数据结构】“单链表”的练习题(二)

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

沁恒ch32V208处理器开发(三)GPIO控制

目录 GPIO功能概述 CH32V2x 微控制器的GPIO 口可以配置成多种输入或输出模式&#xff0c;内置可关闭的上拉或下拉电阻&#xff0c;可以配置成推挽或开漏功能。GPIO 口还可以复用成其他功能。端口的每个引脚都可以配置成以下的多种模式之一&#xff1a; 1 浮空输入 2 上拉输入…