行为型设计模式04-状态模式

news2024/7/6 17:57:52

✨作者:猫十二懿

❤️‍🔥账号:CSDN 、掘金 、个人博客 、Github

🎉公众号:猫十二懿

状态模式

1、状态模式介绍

状态模式(State)是一种行为型设计模式,当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。(可以说当到饭点了,你就会主动去找饭吃)

1.1 存在问题

通常情况下,一个对象的行为会随着其内部状态的改变而发生变化,这些行为通常被包含在相应的if...else 语句中,导致代码难以维护和扩展。

1.2 解决问题

适用状态模式判断状态是否改变。

状态模式的思想是将每种可能的状态都封装成一个类,因此可以在不改变原有代码的前提下动态地改变对象的行为。状态模式提供了一种简单的实现方式,即通过定义一个state接口,再定义具体的state子类,Context类中持有一个state的引用,在不同状态下,分别委托给不同的state处理相应的请求。这种实现方式将原来的大类拆分成多个小类,使得系统更加灵活、易于扩展,符合开闭原则。

状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。当然,如果这个状态判断很简单,那就没必要用状态模式了。

1.3 状态模式结构图

image-20230507103210522

  1. State类,抽象状态类,定义一个接口以封装与Context的一个特定状态相关的行为。

    /**
     * @author Shier
     * CreateTime 2023/5/7 10:39
     * 抽象状态类
     */
    public abstract class State {
        public abstract void  handle(Context context);
    }
    
  2. Context类,维护一个ConcreteState子类的实例,这个实例定义当前的状态。

    /**
     * @author Shier
     * CreateTime 2023/5/7 10:41
     *
     */
    public class Context {
        private State state;
    
        /**
         * 初始化当前状态
         * @param state
         */
        public Context(State state) {
            this.state = state;
        }
    
        public State getState() {
            return state;
        }
    
        public void setState(State state) {
            this.state = state;
            System.out.println("当前状态:"+this.state.getClass().getName());
        }
    
        /**
         * 对请求做处理,并设置下一个状态
         */
        public void request(){
            this.state.handle(this);
        }
    }
    
  3. ConcreteState类,具体状态,每一个子类实现一个与Context的一个状态相关的行为。

    /**
     * @author Shier
     * CreateTime 2023/5/7 10:40
     * 具体状态类A
     */
    public class ConcreteStateA extends State{
    
        /**
         * 设置ConcreteStateA的下一个状态是ConcreteStateB
         * @param context
         */
        @Override
        public void handle(Context context) {
            context.setState(new ConcreteStateB());
        }
    }
    
    /**
     * @author Shier
     * CreateTime 2023/5/7 10:45
     * 具体状态类B
     */
    public class ConcreteStateB extends State {
    
        /**
         * 设置ConcreteStateB的下一个状态是ConcreteStateA
         * @param context
         */
        @Override
        public void handle(Context context) {
            context.setState(new ConcreteStateA());
        }
    }
    
  4. 客户端发起请求调用

    /**
     * @author Shier
     * CreateTime 2023/5/7 10:46
     */
    public class ClientTest {
        public static void main(String[] args) {
            // 初始状态为ConcreteStateA
            Context context = new Context(new ConcreteStateA());
            // 不断请求,不断改变请求状态
            context.request();
            context.request();
            context.request();
        }
    }
    

    输出结果:

    image-20230507105926080

2、具体案例说明状态模式

案例:不同的工作时间做不同的事情

2.1 不使用状态模式

/**
 * @author Shier
 * CreateTime 2023/5/7 11:01
 * 工作类
 */
public class Work {
    // 时间
    private int hour;

    // 是否完成工作任务
    private boolean workFinished = false;

    public int getHour() {
        return hour;
    }

    public void setHour(int hour) {
        this.hour = hour;
    }

    public boolean getWorkFinished() {
        return workFinished;
    }

    public void setWorkFinished(boolean workFinished) {
        this.workFinished = workFinished;
    }

    /**
     * 工作时间段
     */
    public void workTime() {
        if (hour < 12) {
            System.out.println("当前时间" + hour + "点,上午工作,精神百倍。");
        } else if (hour < 13) {
            System.out.println("当前时间" + hour + "点,饿了,午饭,犯困,午休。");
        } else if (hour < 17) {
            System.out.println("当前时间" + hour + "点,下午状态还可以,继续敲代码。");
        } else {
            if (workFinished) {
                System.out.println("当前时间" + hour + "点,下班回家了!!,愉快结束一天。");
            } else {
                if (hour < 21) {
                    System.out.println("当前时间" + hour + "点,又开始加班,疲累之极,**加班。");
                } else {
                    System.out.println("当前时间" + hour + "点,睡觉时间到了,躺床就睡着。");
                }
            }
        }
    }
}

客户端

/**
 * @author Shier
 * CreateTime 2023/5/7 11:09
 */
public class WorkClient {
    public static void main(String[] args) {
        Work work = new Work();
        // 早上
        work.setHour(9);
        work.workTime();
        work.setHour(11);
        work.workTime();
        // 中午
        work.setHour(12);
        work.workTime();
        //下午
        work.setHour(13);
        work.workTime();
        work.setHour(14);
        work.workTime();

        work.setHour(17);
        //工作未完成
        work.setWorkFinished(false);
        // 加班
        work.workTime();

        work.setHour(19);
        work.workTime();
        work.setHour(22);
        work.workTime();
    }
}

结果显示:

image-20230507115020525

有没有发现什么问题?workTime()这个方法已经违背了开闭原则了,每次都要修改这个方法才得以扩展新的功能

2.2 使用状态模式

具体的结构类图

image-20230507120141755

State类:

/**
 * @author Shier
 * CreateTime 2023/5/7 12:02
 * 状态类
 */
public abstract class State {
    public abstract void workTime(Work work);
}

Work工作类:

/**
 * @author Shier
 * CreateTime 2023/5/7 11:01
 * 工作类
 */
public class Work {
    /**
     * 时间点
     */
    private int hour;

    /**
     * 是否完成工作任务-是否到达下班条件
     */
    private boolean workFinished = false;

    /**
     * 当前状态-设置下一个状态
     */
    private State currentState;

    /**
     * 初始化状态-上午
     */
    public Work() {
        currentState = new ForenoonState();
    }

    /**
     * 工作时间段-显示当前状态,并切换到下一个状态
     */
    public void workTime() {
        this.currentState.workTime(this);
    }

    public State getCurrentState() {
        return currentState;
    }
    public void setCurrentState(State currentState) {
        this.currentState = currentState;
    }

    public int getHour() {
        return hour;
    }

    public void setHour(int hour) {
        this.hour = hour;
    }

    public boolean getWorkFinished() {
        return workFinished;
    }

    public void setWorkFinished(boolean workFinished) {
        this.workFinished = workFinished;
    }
}

早上具体状态:

/**
 * @author Shier
 * CreateTime 2023/5/7 12:02
 * 早上具体状态
 */
public class ForenoonState extends State {
    @Override
    public void workTime(Work work) {
        if (work.getHour() < 12) {
            System.out.println("当前时间" + work.getHour() + "点,上午工作,精神百倍。");
        } else {
            // 超过12点就转入中午状态
            work.setCurrentState(new NoonState());
            work.workTime();
        }
    }
}

中午具体状态:

/**
 * @author Shier
 * CreateTime 2023/5/7 12:10
 * 中午状态
 */
public class NoonState extends State {
    @Override
    public void workTime(Work work) {
        if (work.getHour() < 13) {
            System.out.println("当前时间" + work.getHour() + "点,饿了,午饭,犯困,午休。");
        } else {
            // 超过13点就转入下午工作状态
            work.setCurrentState(new AfternoonState());
            work.workTime();
        }
    }
}

下午具体状态:

/**
 * @author Shier
 * CreateTime 2023/5/7 12:12
 * 下午具体状态
 */
public class AfternoonState extends State {
    @Override
    public void workTime(Work work) {
        if (work.getHour() < 17) {
            System.out.println("当前时间" + work.getHour() + "点,下午状态还可以,继续敲代码。");
        } else {
            // 超时17点就进去傍晚工作时间点
            work.setCurrentState(new EveingState());
            work.workTime();
        }
    }
}

任务完成,按时下班状态类

/**
 * @author Shier
 * CreateTime 2023/5/7 12:15
 * 按时下班
 */
public class RestState extends State {
    @Override
    public void workTime(Work work) {
        System.out.println("当前时间:" + work.getHour() + "点,下班回家咯!!");
    }
}

工作任务未完成,加班“累”:

/**
 * @author Shier
 * CreateTime 2023/5/7 12:13
 * 具体加班类
 */
public class EveingState extends State {
    @Override
    public void workTime(Work work) {
        if (work.getWorkFinished()) {
            // 工作完成,下班
            work.setCurrentState(new RestState());
            work.workTime();
        } else {
            // 工作没有完成则继续加班
            if (work.getHour() < 21) {
                System.out.println("当前时间" + work.getHour() + "点,又开始加班,疲累之极,**加班。");
            } else {
                // 到点睡觉
                work.setCurrentState(new SleepingState());
                work.workTime();
            }
        }
    }
}

睡觉类:

/**
 * @author Shier
 * CreateTime 2023/5/7 12:15
 * 睡觉状态
 */
public class SleepingState extends State {
    @Override
    public void workTime(Work work) {
        System.out.println("当前时间" + work.getHour() + "点,睡觉时间到了,躺床就睡着。");
    }
}

客户端同上

最终的结果也同上

虽然结果相同,但是我们的程序变得更加灵活,比如公司要求在20点之前必须离开公司, 此时我们就要新增一个“强制下班类”,并改动一下 “晚间工作状态”类的判断就可以 了。而这是不影响其他状态的代码的。实现加班类修改如下:

/**
 * @author Shier
 * CreateTime 2023/5/7 12:13
 * 具体加班类
 */
public class EveingState extends State {
    @Override
    public void workTime(Work work) {
        if (work.getWorkFinished()) {
            // 工作完成,下班
            work.setCurrentState(new RestState());
            work.workTime();
        } else {
            // 工作没有完成则继续加班
            work.setCurrentState(new ForcedLiveWork());
            work.workTime();
        }
    }
}

强制下班类:

/**
 * @author Shier
 * CreateTime 2023/5/7 12:27
 * 强制下班
 */
public class ForcedLiveWork extends State {
    @Override
    public void workTime(Work work) {
        if (work.getHour() < 20) {
            System.out.println("当前时间" + work.getHour() + "点,公司规定,此刻必须要离开公司了。");
        } else {
            // 到点睡觉
            work.setCurrentState(new SleepingState());
            work.workTime();
        }
    }
}

实现起来并不困难,只需增加一个类,再对去修改判断条件,这样就不会影响到其他状态的代码

3、状态模式总结

状态模式的优点包括:

  1. 将状态转换和行为隔离开来,使得状态变化时只需要改变状态类对象即可,无需修改Context类,从而保证了系统的灵活性、可扩展性和可维护性。
  2. 在状态模式中,每个状态都被封装在一个类中,增加新的状态类很方便,符合开闭原则。
  3. 通过引入抽象状态类和抽象环境类,可以很好地解决代码的耦合问题。状态模式通过把各种状态转移逻辑分布到State的子类之间,来减少相互间的依赖
  4. 状态模式使得状态转换显式化了,独立于具体的状态类之外,更符合面向对象的设计思想。
  5. 对于状态机的实现,状态模式提供了一种设计思路和方法。
  6. 将特定的状态相关的行为都放入一个对象中,由于所有与 状态相关的代码都存在于某个ConcreteState中,所以通过定义新的子类可 以很容易地增加新的状态和转换。(消除大量的条件判断语句)

状态模式的缺点包括:

  1. 由于引入了多个子类,因此在一定程度上增加了系统的复杂度,使得系统抽象层次增加,设计难度加大。
  2. 如果状态改变很频繁,则会导致系统中类的数量增加,从而增加系统的维护难度。
  3. 如果状态比较多,且状态之间的转换比较复杂,容易造成代码的混乱和不易维护。

状态模式适用场景:

  1. 当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态 改变它的行为时,可以考虑使用状态模式。
  2. 当对象的行为随着对象内部状态的变化而发生变化时,可以考虑使用状态模式。例如,电视机有开机、关机、切换频道等状态,根据不同的状态做出相应的响应,使用状态模式可以方便地实现。
  3. 当系统中存在多种状态且状态之间存在转换关系时,可以考虑使用状态模式。例如,一个产品订单在待支付、已支付、已取消等多个状态之间转换,可以使用状态模式来实现。
  4. 当需要对状态进行动态修改时,可以考虑使用状态模式。例如,在游戏中,玩家角色根据当前的状态有不同的技能和装备,而这些状态是可以通过游戏中获得的物品进行修改的,这时候就可以使用状态模式。
  5. 当状态转换规则比较复杂或需要进行扩展时,可以考虑使用状态模式。状态模式将状态转换规则封装在具体状态类中,可以方便地对状态转换规则进行修改和扩展。
  6. 当希望避免使用大量的if else语句,提高代码可读性和可维护性时,可以考虑使用状态模式。状态模式使得代码易于扩展和修改,并且降低了代码的耦合度,更符合面向对象的设计原则。

状态模式适用场景:

  1. 当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态 改变它的行为时,可以考虑使用状态模式。
  2. 当对象的行为随着对象内部状态的变化而发生变化时,可以考虑使用状态模式。例如,电视机有开机、关机、切换频道等状态,根据不同的状态做出相应的响应,使用状态模式可以方便地实现。
  3. 当系统中存在多种状态且状态之间存在转换关系时,可以考虑使用状态模式。例如,一个产品订单在待支付、已支付、已取消等多个状态之间转换,可以使用状态模式来实现。
  4. 当需要对状态进行动态修改时,可以考虑使用状态模式。例如,在游戏中,玩家角色根据当前的状态有不同的技能和装备,而这些状态是可以通过游戏中获得的物品进行修改的,这时候就可以使用状态模式。
  5. 当状态转换规则比较复杂或需要进行扩展时,可以考虑使用状态模式。状态模式将状态转换规则封装在具体状态类中,可以方便地对状态转换规则进行修改和扩展。
  6. 当希望避免使用大量的if else语句,提高代码可读性和可维护性时,可以考虑使用状态模式。状态模式使得代码易于扩展和修改,并且降低了代码的耦合度,更符合面向对象的设计原则。

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

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

相关文章

python---条件语句(1)

顺序语句 按照写的顺序执行 条件语句 条件语句的一些注意事项: 1.情况1 2.情况2 bbb已经不属于条件语句中的内容了 3.情况3 通常使用4个空格或一个制表符tab来表示! if语句的嵌套 当有多级条件嵌套时,当前的语句属于哪个代码块,完全取决于缩进的级别.

SOFA Weekly|SOFAArk 社区会议预告、Layotto 社区会议回顾与预告、社区本周贡献

SOFA WEEKLY | 每周精选 筛选每周精华问答&#xff0c;同步开源进展 欢迎留言互动&#xff5e; SOFAStack&#xff08;Scalable Open Financial Architecture Stack&#xff09;是蚂蚁集团自主研发的金融级云原生架构&#xff0c;包含了构建金融级云原生架构所需的各个组件&am…

PostgreSql根据给的时间范围统计15分钟粒度、小时粒度、天粒度、周粒度、月粒度工单

PostgreSql根据给的时间范围统计15分钟粒度、小时粒度、天粒度、周粒度、月粒度工单 说明实现15分钟粒度工单统计小时粒度工单统计天粒度工单统计周粒度工单统计月粒度工单统计 说明 项目有个需求是统计故障工单每15分钟、每小时、每天、每周和每月共有多少工单。 这里先做个笔…

JavaEE进阶(统一功能处理)6/2

目录 1.使用拦截器实现用户登录的校验功能 2.统一数据格式的返回 3.统一异常的返回 1.使用拦截器实现用户登录的校验功能 Spring拦截器 关键步骤1.实现HandlerInterceptor接口 2.重写preHeadler方法&#xff0c;在方法中编写自己的业务代码 将拦截器添加到配置文件中&#xf…

【剑指offer】二维数组中的查找(详细解析)

文章目录 题目思路代码实现 题目 题目链接入口&#xff1a;牛客&#xff1a;JZ4 二维数组中的查找 思路 1.核心考点 &#xff08;1&#xff09; 数组相关&#xff1a;二维数组&#xff08;矩阵&#xff09;。 &#xff08;2&#xff09; 特性观察&#xff1a;在一个二维数组…

如何使用宝塔面板搭建网站(最后一步!上传文件至宝塔面板)

这里就是我们搭建宝塔面板的最后一步了&#xff0c;把我们的PHP文件上传到宝塔面板上&#xff0c;就可以通过因特尔网络访问我们的网址了。 第一步&#xff1a;基础安装 必须是Linux服务器 不支持window服务器 最低要求配置1核1G当然再低些也能运行但是不建议 要求服务器环…

值得收藏 | 脑机交互作用研究

神经损伤和疾病对许多人的生活产生了巨大的影响&#xff0c;导致了许多运动障碍和日常任务无法独立完成。皮质假体系统通过脑机接口&#xff08;BCI&#xff09;接收一个动作命令来执行所需的位置&#xff0c;从而使得经历神经损伤的人能够实现部分功能恢复。BCI技术可以在侵入…

chatgpt赋能python:Python创建二维列表的方法

Python创建二维列表的方法 Python是一种高级编程语言&#xff0c;它的灵活性和功能强大的库使其成为数据科学和机器学习的最佳工具之一。其中一个常用的数据结构是二维列表&#xff0c;本文将介绍如何在Python中创建一个二维列表。 什么是二维列表&#xff1f; 在Python中&a…

华为OD机试真题 Java 实现【表示数字】【牛客练习题】

一、题目描述 将一个字符串中所有的整数前后加上符号“*”&#xff0c;其他字符保持不变。连续的数字视为一个整数。 数据范围&#xff1a;字符串长度满足1≤n≤100 。 二、输入描述 输入一个字符串。 三、输出描述 字符中所有出现的数字前后加上符号“*”&#xff0c;其…

arcgis for javascript中的TileLayer(缓存地图服务图层)

一、什么是图层 要理解TileLayer图层&#xff0c;咱要先搞清楚在arcgis中图层的概念&#xff1a; ArcGIS for JavaScript中的图层是数据的可视化链接。简单来说&#xff0c;它可以将数据显示在地图上。 图层是地图上的一个图形单元&#xff0c;可以是点、线、面、文本等&#…

复杂的HANASQL 列表转多行

一 前言 基于HANA的内存数据库的强大性能, SAP建议把业务逻辑下沉到HANA中计算.以便减少应用服务器的负担,让程序性能更好一些. SAP本身的一些复杂的业务逻辑比如MRP运算(MD01)也有了新的事务 MD01N (MRP LIVE),性能得以巨大的提升 报表类的数据分析程序尤其适用此原则. 动态报…

淘宝监控竞品sku数据接口

电商竞品数据监控查询可以通过以下几个步骤实现&#xff1a; 确定需要监控的竞品&#xff1a;首先需要明确自己店铺的产品定位和竞争对手&#xff0c;选择需要监控的竞品。 选择监控工具&#xff1a;根据需求和预算选择适合自己的电商竞品数据监控工具&#xff0c;例如谷歌分析…

nvm管理node的多版本,任意安装,切换不同nodejs版本

1.nvm安装包下载&#xff1a; https://github.com/coreybutler/nvm-windows/releases window操作系统选择安装包直接安装&#xff1a; 如果已经在使用的nodejs无需卸载&#xff0c;安装过程中会提示是否需要管理已经安装的版本&#xff0c;选择“是”。 2.安装完成之后&…

云计算——云计算部署形成及应用

作者简介&#xff1a;一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​ 目录 前言 一.云计算部署形式 1.私有云 &#xff08;1&#xff09;私有云优点 &#x…

计划

img { margin: auto; display: block } 优化器的作用是什么&#xff1f;例举一下神经网络中常用的优化器&#xff1f; 优化器作用&#xff1a;求出让损失函数最小化的参数。 常用优化器&#xff1a; 1、Adam 关联所有样本的梯度&#xff0c;便于求解全局最优解&#xff0c;始终…

有公网IP,如何设置端口映射实现访问?

很多中小型公司或个人会根据自身需求自建服务器&#xff0c;或者将自己内网的服务、应用发布到外网&#xff0c;实现异地访问&#xff0c;如远程桌面、网站、数据库、公司的管理系统、FTP、管家婆、监控系统等等。 没接触过的人可能会觉得这个很难&#xff0c;实际上使用快解析…

国内镜像+JDK下载地址+IDEA专业安装版和免安装版下载地址

一、OracleJDK下载地址 JDK版本Oracle JDK新增特性官网原版下载地址JDK 1.0-已下线JDK 1.1-已下线JDK 1.2-已下线JDK 1.3-已下线JDK 1.4-已下线JDK 5.0自动装箱、泛型、枚举、增强的for循环、注解等已下线JDK 6JDBC 4.0、Pluggable Annotation Processing API、JAX-WS、StAX等…

Redis底层学习(六)—存储类型-ZSet篇

这里写目录标题 结构特点具体服务器操作命令底层结构应用场景 结构特点 适⽤场景&#xff1a;存储不重复且有序需求的数据&#xff0c;⽐如&#xff1a;学⽣的⾼考成绩。 它的内部采⽤“ 跳跃列表 ”实现&#xff0c;根据score进⾏排序 具体服务器操作命令 添加元素到zset中…

RedditVideoMakerBot 视频制作机器人自动生成视频搭建教程

https://github.com/elebumm/RedditVideoMakerBot搭建教程 RedditVideoMakerBot视频制作机器人 有些在抖音、快手上的视频获得了数百万的观看次数&#xff0c;你仔细分析他们的视频&#xff0c;他们唯一做的原创事情收集材料、然后拼接在一起。尤其是一些新闻类的视频&#xff…

深度学习-RepVGGNet

文章目录 前言一、RepVgg简介二、为什么训练时采用多分支结构三、为什么推理时使用单分支结构四、结构重参数化1、融合Conv2d和BN 前言 看yolonas代码&#xff0c;发现有QARepVgg网络&#xff0c;完全不懂&#xff0c;所以来补课&#xff0c;先看了RepVGG 这篇文章主要来自于霹…