Windows环境下实现设计模式——状态模式(JAVA版)

我是荔园微风,作为一名在IT界整整25年的老兵,今天总结一下Windows环境下如何编程实现状态模式(设计模式)。
不知道大家有没有这样的感觉,看了一大堆编程和设计模式的书,却还是很难理解设计模式,无从下手。为什么?因为你看的都是理论书籍。
我今天就在Windows操作系统上安装好JAVA的IDE编程工具,并用JAVA语言来实现一个状态模式,真实的实现一个,你看懂代码后,自然就明白了。
状态模式State Pattern(行为型设计模式)
定义:允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。
上面定义听懂了吗?莫名其妙看不懂对吧。所以我们还是来看看实现生活中的例子。
在日常生活中,人们总是有些各种喜怒哀乐的表情,而且这四种状态还会互相转化,包括人在内,很多事物都具有多种状态,而且在不同状态下会具有不同的行为,这些状态在特定条件下还将发生相互转换。而状态模式就是要模拟这种同一事物存在多种状态但又会变化转化的情况。状态模式是一种较为复杂的设计模式,它用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题。当系统中的某个对象存在多个状态,这些状态之间可以进行转换,而且对象在不同状态下行为不相同时可以使用状态模式。
在软件系统中,有些对象具有多种状态,这些状态在某些情况下能够相互转换,而且对象在不同的状态下也将具有不同的行为。通常可以使用复杂的条件判断语句if else之类来进行状态判断和转换操作,但这样会导致代码的可维护性和灵活性下降。
状态模式用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题。当系统中某个对象存在多个状态,这些状态之间可以进行转换,而且对象在不同状态下行为不相同时可以使用状态模式。状态模式将一个对象的状态从该对象中分离出来,封装到专门的状态类中,使得对象状态可以灵活变化,对于客户端而言,无须关心对象状态的转换以及对象所处的当前状态,无论对于何种状态的对象,客户端都可以一致处理。

在状态模式结构图中包含如下三个角色:Context(环境类)、State(抽象状态类)、ConcreteState(具体状态类)。环境类又称为上下文类,它是拥有多种状态的对象。由于环境类的状态存在多样性且在不同状态下对象的行为有所不同,因此将状态独立出去形成单独的状态类。在环境类中维护一个抽象状态类State的实例,这个实例定义当前状态,在具体实现时,它是一个State子类的对象。抽象状态类用于定义一个接口以封装与环境类的一个特定状态相关的行为,在抽象状态类中声明了各种不同状态对应的方法,而在其子类中实现类这些方法,由于不同状态下对象的行为可能不同,因此在不同子类中方法的实现可能存在不同,相同的方法可以写在抽象状态类中。具体状态类是抽象状态类的子类,每一个子类实现一个与环境类的一个状态相关的行为,每一个具体状态类对应环境的一个具体状态,不同的具体状态类其行为有所不同。
JAVA代码
public abstract class State {//声明抽象业务方法,不同的具体状态类可以不同的实现public abstract void handle();
}public class ConcreteState extends State {public void handle() {//方法具体实现代码}
}public class Context {private State state; //维持一个对抽象状态对象的引用private int value; //其他属性值,该属性值的变化可能会导致对象状态发生变化//设置状态对象public void setState(State state) {this.state = state;}public void request() {//其他代码state.handle(); //调用状态对象的业务方法//其他代码}
}
Context(环境类)实际上是真正拥有状态的对象,这个模式的主要精神就是将Context(环境类)中与状态有关的代码提取出来封装到专门的状态类中。在状态模式结构图中,在Context中定义了一个State对象。Context(环境类)与抽象状态类State之间存在单向关联关系,也可能存在依赖或者其他关联关系。
好,下面是关键的内容了,这部分是这个模式和其他设计模式一个很大的不同点,也是此模式复杂的地方,也就是在状态模式的使用过程中,一个对象的状态之间还可以进行相互转换,通常有两种实现状态转换的方式:
(1) 统一由环境类来负责状态之间的转换,此时,环境类还充当了状态管理器(State Manager)角色,在环境类的业务方法中通过对某些属性值的判断if else之类实现状态转换,还可以提供一个专门的方法用于实现属性判断和状态转换,如下所示:
public void changeState() {//判断属性值,根据属性值进行状态转换if (value == 0) {this.setState(new ConcreteStateA());}else if (value == 1) {this.setState(new ConcreteStateB());}......
}
(2) 由具体状态类来负责状态之间的转换,可以在具体状态类的业务方法中判断环境类的某些属性值再根据情况为环境类设置新的状态对象,实现状态转换,同样,也可以提供一个专门的方法来负责属性值的判断和状态转换。此时,状态类与环境类之间就将存在依赖或关联关系,因为状态类需要访问环境类中的属性值,如下所示:
public void changeState(Context su) {//根据环境对象中的属性值进行状态转换if (su.getValue() == 1) {su.setState(new ConcreteStateB());}else if (su.getValue() == 2) {su.setState(new ConcreteStateC());}......
}
应用实例
某金融理财手机APP具有理财功能,每位用户在其中会开通个人账户(Account),APP用户账户存在三种状态,在不同状态下账户存在不同的行为,如果账户中余额大于等于0,则账户的状态为正常状态(Normal State),此时用户既可以向该账户存款也可以从该账户取款; 如果账户中余额小于0,并且大于-10000,则账户的状态为透支状态(Overdraft State),此时用户既可以向该账户存款也可以从该账户取款,但需要按天计算利息;如果账户中余额等于-10000,那么账户的状态为受限状态(Restricted State),此时用户只能向该账户存款,不能再从中取款,同时也将按天计算利息。还有最后一种情况,如果取款导致金额小于-10000元了怎么办?我们这里要规定一下,如果小于这个数,马上宣布不正常状态不准用户操作。Check()用于在每一次执行存款和取款操作后根据余额来判断是否要进行状态转换并实现状态转换,相同的方法在不同的状态中可能会有不同的实现。为了实现不同状态下对象的各种行为以及对象状态之间的相互转化。
(1)Account:银行账户,环境类
package designpatterns.state;public class Account {private AccountState state; //维持一个对抽象状态对象的引用private String owner; //开户名private double balance = 0; //账户余额public Account(String owner,double init) {this.owner = owner;this.balance = init;this.state = new NormalState(this); //设置初始状态System.out.println(this.owner + "开户,余额为" + this.balance); }public double getBalance() {return this.balance;}public void setBalance(double balance) {this.balance = balance;}public void setState(AccountState state) {this.state = state;}public void deposit(double amount) {System.out.println(this.owner + "存款" + amount);state.deposit(amount); //调用状态对象的deposit()方法System.out.println("余额为"+ this.balance);System.out.println("帐户状态为"+ this.state.getClass().getName());}public void withdraw(double amount) {System.out.println(this.owner + "取款" + amount);state.withdraw(amount); //调用状态对象的withdraw()方法System.out.println("余额为"+ this.balance);System.out.println("帐户状态为"+ this. state.getClass().getName()); }public void Interest(){state.Interest(); //调用状态对象的Interest()方法}
}
(2)AccountState:账户状态类,抽象状态类
package designpatterns.state;public abstract class AccountState {protected Account cp;public abstract void deposit(double amount);public abstract void withdraw(double amount);public abstract void Interest();public abstract void Check();
}
(3)NormalState:正常状态类
package designpatterns.state;public class NormalState extends AccountState {public NormalState(Account cp) {this.cp = cp;}public NormalState(AccountState state) {this.cp = state.cp;}public void deposit(double amount) {cp.setBalance(cp.getBalance() + amount);Check();}public void withdraw(double amount) {cp.setBalance(cp.getBalance() - amount);Check();}public void Interest(){System.out.println("正常状态");}//状态转换public void Check() {if (cp.getBalance() > -10000 && cp.getBalance() <= 0) {cp.setState(new OverdraftState(this));}else if (cp.getBalance() == -10000) {cp.setState(new RestrictedState(this));}else if (cp.getBalance() < -10000) {System.out.println("不正常状态");} }
}
(4)OverdraftState:透支状态类
package designpatterns.state;public class OverdraftState extends AccountState {public OverdraftState(AccountState state) {this.cp = state.cp;}public void deposit(double amount) {cp.setBalance(cp.getBalance() + amount);Check();}public void withdraw(double amount) {cp.setBalance(cp.getBalance() - amount);Check();}public void Interest(){System.out.println("利息数额");}//状态转换public void Check() {if (cp.getBalance() > 0) {cp.setState(new NormalState(this));}else if (cp.getBalance() == -10000) {cp.setState(new RestrictedState(this));}else if (cp.getBalance() < -10000) {System.out.println("不正常状态");} }
}
(5)RestrictedState:透支状态类
package designpatterns.state;public class RestrictedState extends AccountState {public OverdraftState(AccountState state) {this.cp = state.cp;}public void deposit(double amount) {cp.setBalance(cp.getBalance() + amount);Check();}public void withdraw(double amount) {System.out.println("不能");}public void Interest(){System.out.println("利息数额");}//状态转换public void Check() {if (cp.getBalance() > 0) {cp.setState(new NormalState(this));}else if (cp.getBalance() > -10000) {cp.setState(new OverdraftState(this));}}
}
(6)Client:客户端测试类
package designpatterns.state;public class Client {public static void main(String args[]) {Account cp = new Account("小牛",0.0);cp.deposit(5000);cp.withdraw(10000);cp.deposit(6000);cp.withdraw(4000);cp.Interest();}
}
这篇设计模式文章是我写的最累的一篇了,写到这里已经很晚了,实在写不动了。输出结果大家自行推理就是了。无非就是随着用户又取钱又存钱的导致银行帐户出现各类状态转换问题。
状态模式将一个对象在不同状态下的不同行为封装在一个个状态类中,通过设置不同的状态对象可以让环境对象拥有不同的行为,而状态转换的细节对于客户端而言是透明的,方便了客户端的使用。在实际开发中,状态模式具有较高的使用频率,在工作流和游戏开发中状态模式都得到了广泛的应用,例如公文状态的转换、游戏中角色的升级等。
各位小伙伴,这次我们就说到这里,下次我们再深入研究windows环境下的各类设计模式实现。
作者简介:荔园微风,1981年生,高级工程师,浙大工学硕士,软件工程项目主管,做过程序员、软件设计师、系统架构师,早期的Windows程序员,Visual Studio忠实用户,C/C++使用者,是一位在计算机界学习、拼搏、奋斗了25年的老将,经历了UNIX时代、桌面WIN32时代、Web应用时代、云计算时代、手机安卓时代、大数据时代、ICT时代、AI深度学习时代、智能机器时代,我不知道未来还会有什么时代,只记得这一路走来,充满着艰辛与收获,愿同大家一起走下去,充满希望的走下去。
相关文章:

Windows环境下实现设计模式——状态模式(JAVA版)
我是荔园微风,作为一名在IT界整整25年的老兵,今天总结一下Windows环境下如何编程实现状态模式(设计模式)。不知道大家有没有这样的感觉,看了一大堆编程和设计模式的书,却还是很难理解设计模式,无…...
【总结】多个条件排序(pii/struct/bool)
目录 pii struct bool pii 现在小龙同学要吃掉它们,已知他有n颗苹果,并且打算每天吃一个。 但是古人云,早上金苹果,晚上毒苹果。由此可见,早上吃苹果和晚上吃苹果的效果是不一样的。 已知小龙同学在第 i 天早上吃苹果能…...

基于stm32mp157 linux开发板ARM裸机开发教程Cortex-A7 开发环境搭建(连载中)
前言:目前针对ARM Cortex-A7裸机开发文档及视频进行了二次升级持续更新中,使其内容更加丰富,讲解更加细致,全文所使用的开发平台均为华清远见FS-MP1A开发板(STM32MP157开发板)针对对FS-MP1A开发板ÿ…...

最适合游戏开发的语言是什么?
建议初学者学习主流的开发技术 主流开发技术有大量成熟的教程、很多可以交流的学习者、及时的学习反馈等;技术的内里基本都是相同的,学习主流技术的经验、知识可以更好更快地疏通学习新知识和技术。 因此,对C#或者C二选一进行学习较好。 Un…...

C语言刷题(7)(字符串旋转问题)——“C”
各位CSDN的uu们你们好呀,今天,小雅兰的内容依旧是复习之前的知识点,那么,就是做一道小小的题目啦,下面,让我们进入C语言的世界吧 实现一个函数,可以左旋字符串中的k个字符。 例如: A…...

有趣且重要的JS知识合集(18)浏览器实现前端录音功能
1、主题描述 兼容多个浏览器下的前端录音功能,实现六大录音功能: 1、开始录音 2、暂停录音 3、继续录音 4、结束录音 5、播放录音 6、上传录音 2、示例功能 初始状态: 开始录音: 结束录音: 录音流程 …...

面试官:聊聊你知道的跨域解决方案
跨域是开发中经常会遇到的一个场景,也是面试中经常会讨论的一个问题。掌握常见的跨域解决方案及其背后的原理,不仅可以提高我们的开发效率,还能在面试中表现的更加游刃有余。 因此今天就来和大家从前端的角度来聊聊解决跨域常见的几种方式。…...

SpringCloud五大核心组件
Consul 等,提供了搭建分布式系统及微服务常用的工具,如配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性token、全局锁、选主、分布式会话和集群状态等,满足了构建微服务所需的所有解决方案。 服务发现——Netflix Eureka …...
Verilog HDL语言入门(二)
强烈建议用同步设计2.在设计时总是记住时序问题3.在一个设计开始就要考虑到地电平或高电平复位、同步或异步复位、上升沿或下降沿触发等问题,在所有模块中都要遵守它4.在不同的情况下用if和case,最好少用if的多层嵌套(1层或2层比较合适&#…...

Simpleperf详细使用
一、Simpleperf介绍 Simpleperf是一个强大的命令行工具,它包含在NDK中,可以帮助我们分析应用的CPU性能。Simpleperf可以帮助我们找到应用的热点,而热点往往与性能问题相关,这样我们就可以分析修复热点源。 如果您更喜欢使用命令…...

【算法基础】二分图(染色法 匈牙利算法)
一、二分图 1. 染色法 一个图是二分图,当且仅当,图中不含奇数环。在判别一个图是否为二分图⑩,其实相当于染色问题,每条边的两个点必须是不同的颜色,一共有两种颜色,如果染色过程中出现矛盾,则说明不是二分图。 for i = 1 to n:if i 未染色DFS(i, 1); //将i号点染色未…...
Caputo 分数阶微分方程-慢扩散方程初边值问题基于L1 逼近的空间二阶方法及其Matlab程序实现
2.3.3 Caputo 分数阶一维问题基于 L1 逼近的空间二阶方法 考虑如下时间分数阶慢扩散方程初边值问题 { 0 C D t α u ( x , t ) = u...
I.MX6ULL_Linux_驱动篇(29) GPIO驱动
Linux 下的任何外设驱动,最终都是要配置相应的硬件寄存器。所以本篇的 LED 灯驱动最终也是对 I.MX6ULL 的 IO 口进行配置,与裸机实验不同的是,在 Linux 下编写驱动要符合 Linux 的驱动框架。I.MX6U-ALPHA 开发板上的 LED 连接到 I.MX6ULL 的 …...

jupyter的安装和使用
目录 ❤ Jupyter Notebook是什么? notebook jupyter 简介 notebook jupyter 组成 网页应用 文档 主要特点 ❤ jupyter notebook的安装 notebook jupyter 安装有两种途径 1.通过Anaconda进行安装 2.通过pip进行安装 启动jupyter notebook ❤ jupyter …...

Springboot新手开发 Cloud篇
前言: 👏作者简介:我是笑霸final,一名热爱技术的在校学生。 📝个人主页:个人主页1 || 笑霸final的主页2 📕系列专栏:后端专栏 📧如果文章知识点有错误的地方,…...

Linux:函数指针做函数参数
#include <stdio.h> #include <stdlib.h> //创建带有函数指针做参数的函数框架api //调用者要先实现回调函数 //调用者再去调用函数框架 //所谓的回调是指 调用者去调用一个带有函数指针做参数的函数框架,函数框架反过来要调用调用者提供的回调函数 …...

Vue3(递归组件) + 原生Table 实现树结构复杂表格
一、递归组件 什么是递归,Javascript中经常能接触到递归函数。也就是函数自己调用自己。那对于组件来说也是一样的逻辑。平时工作中见得最多应该就是菜单组件,大部分系统里面的都是递归组件。文章中我做了按需引入的配置,所以看不到我引用组…...

ArrayList底层源码解析
Java源码系列:下方连接 http://t.csdn.cn/Nwzed 文章目录前言一、**ArrayList底层结构和源码分析**无参构造调用创建ArrayList集合无参构造总结:发文3个工作日后 up 会把总结放入前言部分,但也诚邀读者总结,可放入评论区有参构造…...

python:DIY字符画的程序使用说明.doc
目录开发环境要求运行方法具体的操作步骤如下:代码示例源码及运行程序下载地址开发环境要求 本系统的软件开发及运行环境具体如下。 操作系统:Windows 7、Windows 10。 Python版本:Python 3.7.0。 开发工具:Python IDLE。 …...

【Python/Opencv】图像权重加法函数:cv2.addWeighted()详解
【Python/Opencv】图像权重加法函数:cv2.addWeighted()详解 文章目录【Python/Opencv】图像权重加法函数:cv2.addWeighted()详解1. 介绍2. API3. 代码示例与效果3.1 代码3.2 效果4. 参考1. 介绍 在OpenCV图像加法cv2.add函数详解详细介绍了图像的加法运…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...
Java 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...

佰力博科技与您探讨热释电测量的几种方法
热释电的测量主要涉及热释电系数的测定,这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中,积分电荷法最为常用,其原理是通过测量在电容器上积累的热释电电荷,从而确定热释电系数…...