【设计模式——学习笔记】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.mzph.cn/news/37629.shtml

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

相关文章

国内芯片厂商创新突破,助力国产替代持续加速

近日&#xff0c;中商产业研究院发布最新研究报告显示&#xff0c;今年1~5月份中国进口集成电路为1865亿件&#xff0c;同比下降19.6%&#xff0c;同比去年5个月累计少进口了455亿颗&#xff0c;平均每天少进口3亿颗。与此同时&#xff0c;英特尔、AMD、美光、三星、SK海力士等…

OSI七层模型和TCP/IP四层模型

OSI七层模型和TCP/IP四层模型 七层模型(OSI) OSI七层模型&#xff08;Open Systems Interconnection Reference Model&#xff09;是一个用于计算机网络体系结构的标准化框架&#xff0c;旨在定义网络通信中不同层次的功能和协议。 各个层次具体如下&#xff1a; 物理层&am…

C语言 冒泡排序

目录 一、原理 二、代码演示 三、代码优化 一、原理 假设&#xff1a; int arr[] { 9,8,7,6,5,4,3,2,1,0 }; 将 arr 内的元素进行升序排列&#xff0c;得到一个新的数组 int arr[] { 0&#xff0c;1&#xff0c;2&#xff0c;3&#xff0c;4&#xff0c;5&#xff0c;…

Python学习笔记_基础篇(二)_数据类型之字符串

一.基本数据类型 整数&#xff1a;int 字符串&#xff1a;str(注&#xff1a;\t等于一个tab键) 布尔值&#xff1a; bool 列表&#xff1a;list 列表用[] 元祖&#xff1a;tuple 元祖用&#xff08;&#xff09; 字典&#xff1a;dict 注&#xff1a;所有的数据类型都存在想对应…

ATF(TF-A)安全通告 TFV-9 (CVE-2022-23960)

ATF(TF-A)安全通告汇总 目录 一、ATF(TF-A)安全通告 TFV-9 (CVE-2022-23960) 二、CVE-2022-23960 一、ATF(TF-A)安全通告 TFV-9 (CVE-2022-23960) Title TF-A披露通过分支预测目标重用&#xff08;branch prediction target reuse&#xff09;引发的前瞻执行处理器漏洞 CV…

【软考】2023系统架构设计师考试

目录 1 软考资格设置 2 考试报名 3 考试准备 4 参加考试 5 考试感受 6 其他 1 软考资格设置 2 考试报名 报名网址&#xff1a;https://www.ruankao.org.cn/ 3 考试准备 4 参加考试 2023年下半年系统架构设计师考试时间为11月4、5日。 5 考试感受 6 其他 最近好像有地区…

vue element 多图片组合预览

定义组件&#xff1a;preview-image <template><div><div class"imgbox"><divclass"preview-img":class"boxClass"v-if"Imageslist 3 ||Imageslist 5 ||Imageslist 7 ||Imageslist 8 ||Imageslist > 9"&…

LangChain手记 Models,Prompts and Parsers

整理并翻译自DeepLearning.AILangChain的官方课程&#xff1a;Models,Prompts and Parsers 模型&#xff0c;提示词和解析器&#xff08;Models, Prompts and Parsers&#xff09; 模型&#xff1a;大语言模型提示词&#xff1a;构建传递给模型的输入的方式解析器&#xff1a;…

即将发布的 Kibana 版本可运行 Node.js 18

作者&#xff1a;Thomas Watson Kibana 构建在 Node.js 框架之上。 为了确保每个 Kibana 版本的稳定性和使用寿命&#xff0c;我们始终将捆绑的 Node.js 二进制文件保持为最新的最新长期支持 (LTS) 版本。 当 Node.js 版本 18 升级到 LTS 时&#xff0c;我们开始将 Kibana 升级…

【爬虫】爬取旅行评论和评分

以马蜂窝“普达措国家公园”为例&#xff0c;其评论高达3000多条&#xff0c;但这3000多条并非是完全向用户展示的&#xff0c;向用户展示的只有5页&#xff0c;数了一下每页15条评论&#xff0c;也就是75条评论&#xff0c;有点太少了吧&#xff01; 因此想了个办法尽可能多爬…

【 BERTopic应用 02/3】 分析卡塔尔世界杯推特数据

摄影&#xff1a;Fauzan Saari on Unsplash 一、说明 这是我们对世界杯推特数据分析的第3部分&#xff0c;我们放弃了。我们将对我们的数据进行情绪分析&#xff0c;以了解人们对卡塔尔世界杯的感受。我将在这里介绍的一个功能强大的工具包是Hugging Face&#xff0c;您可以在…

高忆管理:策略:短期利空落地 市场有望企稳回升

高忆管理指出&#xff0c;基于A股商场出资环境分析&#xff0c;尤其是当时商场存量博弈为主的布景下&#xff0c;主张重视以下“21”主线轮动&#xff1a;&#xff08;1&#xff09;国产科技代替立异&#xff1a;电子&#xff08;半导体、消费电子&#xff09;、通信&#xff0…

Vue基本知识

一、vue入门 Vue为前端的框架&#xff0c;免除了原生js的DOM操作。简化书写。 基于MVVM的思想&#xff0c;实现数据的双向绑定&#xff0c;使编程的重点放在数据上。 1、引入vue.js文件 2、定义vue核心对象&#xff0c;定义数据模型 3、编写视图 //1、引入vue.js <scr…

关于vue,记录一次修饰符.stop和.once的使用,以及猜想。

内置指令 | Vue.js 在vue的api里&#xff0c;关于v-on有stop和once两个事件标签。 .stop - 调用 event.stopPropagation()。.once - 最多触发一次处理函数。 原有主要代码和页面效果 &#xff08;无stop和once&#xff09;: ...<div class"div" click"di…

激活函数总结(九):Softmax系列激活函数补充(Softmin、Softmax2d、Logsoftmax)

激活函数总结&#xff08;九&#xff09;&#xff1a;Softmax系列激活函数补充 1 引言2 激活函数2.1 Softmin激活函数2.2 Softmax2d激活函数2.3 Logsoftmax激活函数 3. 总结 1 引言 在前面的文章中已经介绍了介绍了一系列激活函数 (Sigmoid、Tanh、ReLU、Leaky ReLU、PReLU、S…

React+Typescript清理项目环境

上文 创建一个 ReactTypescript 项目 我们创建出了一个 React配合Ts开发的项目环境 那么 本文 我们先将环境清理感觉 方便后续开发 我们先来聊一下React的一个目录结构 跟我们之前开发的React项目还是有一些区别 public 主要是存放一些静态资源文件 例如 html 图片 icon之类的 …

响应式设计是什么?怎么学习? - 易智编译EaseEditing

响应式设计是一种用于创建能够适应不同设备和屏幕尺寸的网站和应用程序的设计方法。它的目标是确保网站在各种设备上都能提供良好的用户体验&#xff0c;无论是在大屏幕的桌面电脑上还是在小屏幕的移动设备上。 在响应式设计中&#xff0c;页面的布局、字体、图像和其他元素会…

【yml文件的解释】

目录 一、yml的简介二、手写yml文件进行配置三、使用yaml格式导出生成模板四、deployment.yaml文件详解五、Pod yaml文件详解六、Service yaml文件详解 一、yml的简介 Kubernetes 支持 YAML 和 JSON 格式管理资源对象 JSON 格式&#xff1a;主要用于 api 接口之间消息的传递 Y…

路由器和交换机的区别

交换机和路由器的区别 交换机实现局域网内点对点通信&#xff0c;路由器实现收集发散&#xff0c;相当于一个猎头实现的中介的功能 路由器属于网络层&#xff0c;可以处理TCP/IP协议&#xff0c;通过IP地址寻址&#xff1b;交换机属于中继层&#xff0c;通过MAC地址寻址(列表)…

护眼灯值不值得买?什么护眼灯对眼睛好

想要选好护眼台灯首先我们要知道什么是护眼台灯&#xff0c;大的方向来看&#xff0c;护眼台灯就是可以保护视力的台灯&#xff0c;深入些讲就是具备让灯发出接近自然光特性的光线&#xff0c;同时光线不会伤害人眼而出现造成眼部不适甚至是视力降低的照明设备。 从细节上看就…