设计模式-状态模式在Java中的使用示例-信用卡业务系统

场景

在软件系统中,有些对象也像水一样具有多种状态,这些状态在某些情况下能够相互转换,而且对象在不同的状态下也将具有不同的行为。

为了更好地对这些具有多种状态的对象进行设计,我们可以使用一种被称之为状态模式的设计模式。

状态模式用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题。当系统中某个对象存在多个状态,

这些状态之间可以进行转换,而且对象在不同状态下行为不相同时可以使用状态模式。

状态模式将一个对象的状态从该对象中分离出来,封装到专门的状态类中,使得对象状态可以灵活变化,

对于客户端而言,无须关心对象状态的转换以及对象所处的当前状态,无论对于何种状态的对象,客户端都可以一致处理。

状态模式(State Pattern):

允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。其别名为状态对象(Objects for States),

状态模式是一种对象行为型模式。在状态模式中引入了抽象状态类和具体状态类,它们是状态模式的核心,其结构如图所示

在状态模式结构图中包含如下几个角色:

● Context(环境类):

环境类又称为上下文类,它是拥有多种状态的对象。由于环境类的状态存在多样性且在不同状态下对象的行为有所不同,

因此将状态独立出去形成单独的状态类。在环境类中维护一个抽象状态类State的实例,这个实例定义当前状态,

在具体实现时,它是一个State子类的对象。

● State(抽象状态类):

它用于定义一个接口以封装与环境类的一个特定状态相关的行为,在抽象状态类中声明了各种不同状态对应的方法,

而在其子类中实现类这些方法,由于不同状态下对象的行为可能不同,因此在不同子类中方法的实现可能存在不同,

相同的方法可以写在抽象状态类中。

● ConcreteState(具体状态类):它是抽象状态类的子类,每一个子类实现一个与环境类的一个

状态相关的行为,每一个具体状态类对应环境的一个具体状态,不同的具体状态类其行为有所不同。

注:

博客:
霸道流氓气质-CSDN博客

实现

1、示例场景

软件公司欲为某银行开发一套信用卡业务系统,银行账户(Account)是该系统的核心类之一,

通过分析,Sunny软件公司开发人员发现在该系统中,账户存在三种状态,且在不同状态下账户存在不同的行为,

具体说明如下:

(1) 如果账户中余额大于等于0,则账户的状态为正常状态(Normal State),此时用户既可以向该账户存款也可以从该账户取款;

(2) 如果账户中余额小于0,并且大于-2000,则账户的状态为透支状态(Overdraft State),此时用户既可以向该账户存款也可以从该账户取款,

但需要按天计算利息;

(3) 如果账户中余额等于-2000,那么账户的状态为受限状态(Restricted State),此时用户只能向该账户存款,不能再从中取款,

同时也将按天计算利息;

(4) 根据余额的不同,以上三种状态可发生相互转换。

2、新建银行账户类:环境类

/*** 银行账户:环境类*/
public class Account {private AccountState state; //维持一个对抽象状态对象的引用private String owner; //开户名private double balance = 0; //账户余额public Account(String owner,double init){this.owner = owner;this.balance = balance;this.state = new NormalState(this); //设置初始状态System.out.println(this.owner+"开户,初始金额为"+init);System.out.println("------------------------------");}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().getSimpleName());System.out.println("---------------------------------");}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().getSimpleName());System.out.println("---------------------------------");}public void computeInterest(){state.computeInterest(); //调用状态对象的computeInterest方法}
}

3、新建抽象状态类

/*** 抽象状态类*/
abstract class AccountState {protected Account acc;public abstract void deposit(double amount);public abstract void withdraw(double amount);public abstract void computeInterest();public abstract void stateCheck();
}

4、新建正常状态类:具体状态类

/*** 正常状态类:具体状态类*/
public class NormalState extends AccountState{public NormalState(Account acc){this.acc = acc;}public NormalState(AccountState state){this.acc = state.acc;}@Overridepublic void deposit(double amount) {acc.setBalance(acc.getBalance()+amount);stateCheck();}@Overridepublic void withdraw(double amount) {acc.setBalance(acc.getBalance()-amount);stateCheck();}@Overridepublic void computeInterest() {System.out.println("正常状态,无须支付利息!");}//状态转换@Overridepublic void stateCheck() {if(acc.getBalance() > -2000 && acc.getBalance() <=0){acc.setState(new OverdraftState(this));}else if(acc.getBalance() == -2000){acc.setState(new RestrictedState(this));}else if(acc.getBalance()<-2000){System.out.println("操作受限!");}}
}

5、新建透支状态类:具体状态类

//透支状态类:具体状态类
public class OverdraftState extends AccountState {public OverdraftState(AccountState state) {this.acc = state.acc;}@Overridepublic void deposit(double amount) {acc.setBalance(acc.getBalance()+amount);stateCheck();}@Overridepublic void withdraw(double amount) {acc.setBalance(acc.getBalance()-amount);stateCheck();}@Overridepublic void computeInterest() {System.out.println("计算利息");}@Overridepublic void stateCheck() {if(acc.getBalance()>0){acc.setState(new NormalState(this));}else if(acc.getBalance() == -2000){acc.setState(new RestrictedState(this));}else if(acc.getBalance() <-2000){System.out.println("操作受限!");}}
}

6、新建受限状态类:具体状态类

//受限状态:具体状态类
public class RestrictedState extends AccountState {public RestrictedState(AccountState state) {this.acc = state.acc;}@Overridepublic void deposit(double amount) {acc.setBalance(acc.getBalance() + amount);stateCheck();}@Overridepublic void withdraw(double amount) {System.out.println("账号受限,取款失败");}@Overridepublic void computeInterest() {System.out.println("计算利息!");}@Overridepublic void stateCheck() {if(acc.getBalance()>0){acc.setState(new NormalState(this));}else if(acc.getBalance()>-2000){acc.setState(new OverdraftState(this));}}
}

7、编写客户端测试

public class StatePatternClient {public static void main(String[] args) {Account acc = new Account("张三",0.0);acc.deposit(1000);acc.withdraw(2000);acc.deposit(3000);acc.withdraw(4000);acc.withdraw(1000);acc.computeInterest();}
}

测试结果

张三开户,初始金额为0.0
------------------------------
张三存款1000.0
现在余额为1000.0
现在账户状态为NormalState
---------------------------------
张三取款2000.0
现在余额为-1000.0
现在账户状态为OverdraftState
---------------------------------
张三存款3000.0
现在余额为2000.0
现在账户状态为NormalState
---------------------------------
张三取款4000.0
现在余额为-2000.0
现在账户状态为RestrictedState
---------------------------------
张三取款1000.0
账号受限,取款失败
现在余额为-2000.0
现在账户状态为RestrictedState
---------------------------------
计算利息!

Process finished with exit code 0

8、总结

 状态模式将一个对象在不同状态下的不同行为封装在一个个状态类中,通过设置不同的状态对象可以让环境对象拥有不同的行为,

而状态转换的细节对于客户端而言是透明的,方便了客户端的使用。在实际开发中,状态模式具有较高的使用频率,

在工作流和游戏开发中状态模式都得到了广泛的应用,例如公文状态的转换、游戏中角色的升级等。

1. 主要优点

状态模式的主要优点如下:

(1) 封装了状态的转换规则,在状态模式中可以将状态的转换代码封装在环境类或者具体状态类中,可以对状态转换代码进行集中管理,

而不是分散在一个个业务方法中。

(2) 将所有与某个状态有关的行为放到一个类中,只需要注入一个不同的状态对象即可使环境对象拥有不同的行为。

(3) 允许状态转换逻辑与状态对象合成一体,而不是提供一个巨大的条件语句块,状态模式可以让我们避免使用庞大的条件语句来将业务方法和状态转换代码交织在一起。

(4) 可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。

1. 主要缺点

状态模式的主要缺点如下:

(1) 状态模式的使用必然会增加系统中类和对象的个数,导致系统运行开销增大。

(2) 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱,增加系统设计的难度。

(3) 状态模式对“开闭原则”的支持并不太好,增加新的状态类需要修改那些负责状态转换的源代码,

否则无法转换到新增状态;而且修改某个状态类的行为也需修改对应类的源代码。

1. 适用场景

在以下情况下可以考虑使用状态模式

(1) 对象的行为依赖于它的状态(如某些属性值),状态的改变将导致行为的变化。

(2) 在代码中包含大量与对象状态有关的条件语句,这些条件语句的出现,会导致代码的可维护性和灵活性变差,不能方便地增加和删除状态,

并且导致客户类与类库之间的耦合增强。

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

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

相关文章

【提示学习论文】BlackVIP: Black-Box Visual Prompting for Robust Transfer Learning论文原理

BlackVIP: Black-Box Visual Prompting for Robust Transfer Learning BlackVIP:稳健迁移学习的黑盒视觉提示 问题 黑盒白盒&#xff1f; 黑盒和白盒的概念与对预训练模型内部参数的了解程度相关。黑盒指的是对预训练模型的参数和结构缺乏详细了解&#xff0c;通常只能通过使…

OpenCV 如何实现边缘检测器

返回:OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇&#xff1a;OpenCV如何实现拉普拉斯算子的离散模拟 下一篇 :OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 目标 在本教程中&#xff0c;您将学习如何&#xff1a; 使用 OpenCV 函数…

测试用例设计方法-异常测试

飞的最高的海鸥&#xff0c;能看到最远的奇景。大家好&#xff0c;继续给大家分享如何进行异常测试&#xff0c;首先要做好异常测试&#xff0c;需要我们对被测系统进行全面的了解&#xff0c;熟悉被测系统的功能、架构和运行机制&#xff0c;然后在这个基础上尽可能覆盖各种的…

MATLAB命令

MATLAB是一个用于数值计算和数据可视化的交互式程序。您可以通过在命令窗口的MATLAB提示符 ‘>>’ 处键入命令来输入命令。 在本节中&#xff0c;我们将提供常用的通用MATLAB命令列表。 用于管理会话的命令 MATLAB提供了用于管理会话的各种命令。下表提供了所有此类命令…

递归神经网络(RNN)在AI去衣技术中的深度应用

在人工智能&#xff08;AI&#xff09;技术飞速发展的今天&#xff0c;图像处理和计算机视觉领域不断取得新的突破。其中&#xff0c;AI去衣技术作为一个具有挑战性的研究方向&#xff0c;引起了广大研究者和公众的关注。递归神经网络&#xff08;RNN&#xff09;作为深度学习的…

《Python源码剖析》之PyTypeObject

前言 这一篇博客原本应该是写在上一篇关于pyObject对象的博客中的&#xff0c;但是为了不把内容写的又臭又长&#xff0c;给读者减轻痛苦&#xff0c;给我也减少压力&#xff0c;于是就专门用一篇介绍一下今天的主角—pyTypeObject。 开始 还记得在上一篇我们有聊到&#xf…

vscode 使用文件模板功能来添加版权信息

vscode 新建文件的时候&#xff0c;自动填充作者及版权信息 无需使用插件&#xff0c;操作如下&#xff1a; 选择 “首选项(Preferences)”。在搜索框中输入 “file template” 或者 “文件模板”&#xff0c;然后选择相关的设置项。 {"C_Cpp.clang_format_fallbackSt…

嵌入式虽然入门容易,但精通难度很大。

在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「嵌入式的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01;此外&#xff0c;嵌入式系统的…

蓝桥杯——分巧克力

思路非常简单&#xff0c;就是一个二分法。 注意一下l和r的取值&#xff0c;就可以了。 // 如何进行切分巧克力&#xff1a;横纵除法。例如&#xff1a;一块6*5的&#xff0c;欲切为3*3的小块&#xff0c;横&#xff1a;6/2 3&#xff1b;纵&#xff1a;5/31.所以可以切成3*…

职业技能鉴定服务中心(新闻系统+证书查询系统)

后端采用ThinkPHP8&#xff0c;最新tp框架 前端采用divcss布局 数据库采用MySQL 采用三种技术实现新闻系统和证书查询系统 源码&#xff1a;git clone https://gitee.com/3539949703/certificate-website.git 效果图如下&#xff1a;

[Linux_IMX6ULL驱动开发]-设备树简述

目录 设备树的引入 设备树具体框架 设备树的属性 label address-cells和size-cells compatible model status reg 设备树的编译 内核对设备树的处理 plateform_device如何对应plateform_driver 设备树的引入 之前已经学习了解过了总线驱动模型的概念&#xff0c;也…

webots学习记录8:R2023b如何在某个零件上添加一个恒定的力(矩)

在webots安装路径下&#xff0c;从include\controller\c\webots\supervisor.h中可以看到如下定义&#xff1a; void wb_supervisor_node_add_force(WbNodeRef node, const double force[3], bool relative); void wb_supervisor_node_add_force_with_offset(WbNodeRef node, c…

37-5 基于时间的盲注 SQL 注入 PoC 的 Python 编写

攻击目标就用sql靶场的第9关,手注与靶场搭建:22-5 SQL注入攻击 - 基于时间的盲注-CSDN博客 poc import concurrent.futures # 导入并发模块 import requests # 导入发送HTTP请求的库 import string # 导入处理字符串的库url = "http://127.0.0.1/sqli-labs-master…

AndroidStudio中虚拟机(AVD)无法启动,出现unable to locate adb错误

1.检查Android SDK Platform-Tools是否安装(个人是通过这个方法解决的) 首先通过File-Project Structure-Project SDK检查SDK有没有被选中 步骤&#xff1a;打开file -> settings &#xff0c;搜索SDK 之后点击"-",在点击Apply进行安装 2.可能是驱动的问题 电脑…

深入解析YOLOv2

深入解析YOLOv2 引言 目标检测是计算机视觉中的一个核心问题&#xff0c;它旨在识别图像中所有感兴趣的目标&#xff0c;并给出它们的类别和位置。近年来&#xff0c;随着深度学习技术的发展&#xff0c;目标检测领域取得了巨大的进步。YOLO&#xff08;You Only Look Once&a…

基于OSAL 实现UART、LED、ADC等基础示例 4

1 UART 实验目的 串口在我们开发单片机项目是很重要的&#xff0c;可以观察我们的代码运行情况&#xff0c;本节的目的就 是实现串口双工收发。 虽然说 osal 相关的代码已经跟硬件关系不大了&#xff0c;但是我们还是来贴出相关的硬件原理图贴出来。 1.1 初始化 osal_init_s…

交换式网络捕获网络流量的方法

交换式网络捕获网络流量的方法 参考资料&#xff1a; https://blog.csdn.net/weixin_44143678/article/details/107559329 # 一.端口镜像 端口镜像&#xff0c;又称为“端口监视”或“端口抄送”&#xff0c;是一种网络管理技术&#xff0c;旨在将网络设备上的特定端口的流…

PyTorch Conv2d 前向传递中发生了什么?

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

Springboot的日常操作技巧

文章目录 1、自定义横幅2、容器刷新后触发方法自定义3、容器启动后触发方法自定义**CommandLineRunner**ApplicationRunner 不定时增加 参考文章 1、自定义横幅 简单就一点你需要把banner.text放到classpath 路径下 &#xff0c;默认它会找叫做banner的文件&#xff0c;各种格式…

spring的bean创建流程源码解析

文章目录 IOC 和 DIBeanFactoryApplicationContext实现的接口1、BeanFactory接口2、MessageSource 国际化接口3、ResourcePatternResolver&#xff0c;资源解析接口4、EnvironmentCapable接口&#xff0c;用于获取环境变量&#xff0c;配置信息5、ApplicationEventPublisher 事…