状态模式-对象状态及其转换

 某信用卡业务系统,银行账户存在3种状态,且在不同状态下存在不同的行为:

1)正常状态(余额大等于0),用户可以存款也可以取款;

2)透支状态(余额小于0且大于-2000),用户可以存款也可以取款,但需要对欠款支付利息。

3)受限状态(余额小等于-2000),用户只能存款,还需要对欠款支付利息。

图 伪代码实现上述需求

上面代码存在以下问题:

1)获取状态时,有好多个if分支,如果再增加几个状态,则需要增加判断条件,同时也不符合开闭原则。

2)在进行存取款操作时,有对状态进行判断的条件,行为受到状态的限制。

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

1 状态模式

状态模式(State Pattern)允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的状态类。是一种对象行为型模式。

图 状态模式UML

Context:环境类,是拥有多种状态的对象。由于环境类的状态存在多样性且在不同状态下对象的行为有所不同,因此将状态独立出去形成单独的状态类。

State:抽象状态类,用于定义一个接口以封装与环境类的一个特定状态相关的行为。在抽象状态类中声明各种不同状态对应的方法,而在其子类中实现这些方法。

ConcreteState:具体状态类,是抽象状态类的子类,每个子类实现与环境类的一个状态相关的行为。

public class UserAccount {private double balance;private CardState cardState;public UserAccount(double balance) {this.balance = balance;cardState = new NormalCardState(this);}public double getBalance() {return balance;}public void setBalance(double balance) {this.balance = balance;}public void deposit(double money) {System.out.println("存钱 " + money);cardState.deposit(money);changeState();System.out.println("信用卡余额:"+ balance + ",状态是:" + cardState.getState());System.out.println("------------------------------");}public void withdraw(double money) {if (balance - money < 0) {System.out.println("借钱 " + money + ",利息利率是0.01");} else {System.out.println("取款 " + money);}cardState.withdraw(money);changeState();System.out.println("信用卡余额:"+ balance + ",状态是:" + cardState.getState());System.out.println("------------------------------");}public void changeState() {if (balance > 0) {if (!"正常".equals(cardState.getState())) cardState = new NormalCardState(this);} else if (balance > -2000) {if (!"透支".equals(cardState.getState())) cardState = new OverdraftCardState(this);} else {if (!"受限".equals(cardState.getState())) cardState = new LimitationCardState(this);}}public void setCardState(CardState cardState) {this.cardState = cardState;}
}public abstract class CardState {protected final UserAccount userAccount;public CardState(UserAccount userAccount) {this.userAccount = userAccount;}public abstract void deposit(double money); // 存款public abstract void withdraw(double money); // 取款public abstract void payInterest(); // 支付利息public abstract String getState(); // 获取状态}public class BankService {public static void main(String[] args) {// 开户UserAccount userAccount = new UserAccount(1000);userAccount.withdraw(500);userAccount.deposit(200);userAccount.withdraw(1000);userAccount.deposit(100);userAccount.withdraw(2000);userAccount.withdraw(500);}}//取款 500.0
//信用卡余额:500.0,状态是:正常
//------------------------------
//存钱 200.0
//信用卡余额:700.0,状态是:正常
//------------------------------
//借钱 1000.0,利息利率是0.01
//信用卡余额:-300.0,状态是:透支
//------------------------------
//存钱 100.0
//支付利息:-3.0
//信用卡余额:-203.0,状态是:透支
//------------------------------
//借钱 2000.0,利息利率是0.01
//支付利息:-2.0300000000000002
//信用卡余额:-2205.03,状态是:受限
//------------------------------
//借钱 500.0,利息利率是0.01
//该账户已受限,不能取款
//信用卡余额:-2205.03,状态是:受限
//------------------------------public class NormalCardState extends CardState{public NormalCardState(UserAccount userAccount) {super(userAccount);}@Overridepublic void deposit(double money) {userAccount.setBalance(userAccount.getBalance() + money);}@Overridepublic void withdraw(double money) {userAccount.setBalance(userAccount.getBalance() - money);}@Overridepublic void payInterest() {}@Overridepublic String getState() {return "正常";}}public class OverdraftCardState extends CardState{public OverdraftCardState(UserAccount userAccount) {super(userAccount);}@Overridepublic void deposit(double money) {payInterest();userAccount.setBalance(userAccount.getBalance() + money);}@Overridepublic void withdraw(double money) {payInterest();userAccount.setBalance(userAccount.getBalance() - money);}@Overridepublic void payInterest() {System.out.println("支付利息:" + userAccount.getBalance() * 0.01);userAccount.setBalance(userAccount.getBalance() * ( 1 + 0.01));}@Overridepublic String getState() {return "透支";}
}public class LimitationCardState extends CardState{public LimitationCardState(UserAccount userAccount) {super(userAccount);}@Overridepublic void deposit(double money) {payInterest();userAccount.setBalance(userAccount.getBalance() + money);}@Overridepublic void withdraw(double money) {System.out.println("该账户已受限,不能取款");}@Overridepublic void payInterest() {System.out.println("支付利息:" + userAccount.getBalance() * 0.01);userAccount.setBalance(userAccount.getBalance() * ( 1 + 0.01));}@Overridepublic String getState() {return "受限";}
}

使用状态模式后,在编码过程中,可以不要在关系具体状态,只需专注实现具体状态下的业务。

1.1 状态转换方式

在状态模式中,环境类的状态转换方式有两种:

1)在环境类完成转换。(上面代码是以这种形式)

2)在具体状态类中完成转换。

图 两种状态转换方式的比较

如果新增状态类,则两种方式都需要在各自的类中做修改。都不符合开闭原则。

1.2 共享状态

在有些情况下,多个环境类对象需要共享一个状态,这时需要把状态对象定义为一个静态成员对象。

需求:一个房间有两个开关来控制灯泡的开关。开关等功能是固定的(打开只能使灯泡亮起,关闭只能使灯泡熄灭。

public class LightSwitch {private final static LightState onState = new OnLightState(),offState = new OffLightState();private static LightState lightState = offState;private final String name;public LightSwitch(String name) {this.name = name;}public static void changeLightState(String type) {if ("on".equalsIgnoreCase(type)) {lightState = onState;} else {lightState = offState;}}public void off() {System.out.println(name + "关闭操作");lightState.off(this);}public void on() {System.out.println(name + "打开操作");lightState.on(this);}}public abstract class LightState {public abstract void on(LightSwitch lightSwitch);public abstract void off(LightSwitch lightSwitch);
}public class OnLightState extends LightState{@Overridepublic void on(LightSwitch lightSwitch) {System.out.println("灯泡已打开");System.out.println("--------------");}@Overridepublic void off(LightSwitch lightSwitch) {System.out.println("关闭成功");LightSwitch.changeLightState("off");System.out.println("--------------");}}public class OffLightState extends LightState{@Overridepublic void on(LightSwitch lightSwitch) {System.out.println("打开成功");LightSwitch.changeLightState("on");System.out.println("--------------");}@Overridepublic void off(LightSwitch lightSwitch) {System.out.println("灯泡已关闭");System.out.println("--------------");}}public class PeopleOpera {public static void main(String[] args) {LightSwitch lightSwitch1 = new LightSwitch("开关1");LightSwitch lightSwitch2 = new LightSwitch("开关2");lightSwitch1.on();lightSwitch2.off();lightSwitch1.off();lightSwitch1.on();lightSwitch2.on();lightSwitch2.off();}
}//开关1打开操作
//打开成功
//--------------
//开关2关闭操作
//关闭成功
//--------------
//开关1关闭操作
//灯泡已关闭
//--------------
//开关1打开操作
//打开成功
//--------------
//开关2打开操作
//灯泡已打开
//--------------
//开关2关闭操作
//关闭成功
//--------------

2 优缺点

优点:

1)环境类转换状态方式,封装状态的转换规则,对状态转换代码集中管理。

2)将所有与具体状态有关的行为都封装在一个类中。

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

4)在具体状态类中转换状态方式,将状态转换逻辑与状态对象合成一起,避免使用庞大的条件语句块来将业务方法和状态转换代码交织在一起。

缺点:

1)增加了类和对象的个数。

2)实现较为复杂,增加了系统设计难度。

3)对开闭原则的支持并不好。

3 适用场景

1)对象的行为依赖它的状态,且状态之间互相转换。

2)代码中包含大量与对象状态有关的条件语句。

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

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

相关文章

【数据结构】选择排序

⭐ 作者&#xff1a;小胡_不糊涂 &#x1f331; 作者主页&#xff1a;小胡_不糊涂的个人主页 &#x1f4c0; 收录专栏&#xff1a;浅谈数据结构 &#x1f496; 持续更文&#xff0c;关注博主少走弯路&#xff0c;谢谢大家支持 &#x1f496; 直接选择、堆排序 1. 直接选择排序2…

IconWorkshop 6软件官方下载:制作ICO/ICON图标、编辑、转换图标

我们都知道在这个版本中&#xff0c;你甚至能够打开 Macintosh(R) 图标并将其转换为 Win(R) 格式。我们必须承认加强的与 Adobe(R) 和 Jasc(R) 色彩文件相兼容的色板和调色板管理系统。可以说这些功能随时都方便易用&#xff1a;内置的浏览器能够用缩略图的形式浏览文件&#x…

【PC】2023年10月商店更新-闲游盒

亲爱的玩家朋友们&#xff0c;大家好&#xff01; 欢迎大家来到2023年第十次商店更新&#xff01; 万圣节即将到来&#xff0c;为了让大家可以在万圣节盛装出席&#xff0c;我们在10月商店更新中准备了邪恶诅咒&#xff0c;PGC 2023的专属宝物&#xff0c;以及RASH悲喜强势回归…

tomcat必要的配置

tomcat要配置两个&#xff0c;不然访问不了localhost:8080 名&#xff1a;CATALINA_HOME 值&#xff1a;D:\software\computer_software\Tomcat\tomcat8.5.66

Zynq UltraScale+ XCZU5EV 纯VHDL解码 IMX214 MIPI 视频,2路视频拼接输出,提供vivado工程源码和技术支持

目录 1、前言免责声明 2、我这里已有的 MIPI 编解码方案3、本 MIPI CSI2 模块性能及其优越性4、详细设计方案设计原理框图IMX214 摄像头及其配置D-PHY 模块CSI-2-RX 模块Bayer转RGB模块伽马矫正模块VDMA图像缓存Video Scaler 图像缓存DP 输出 5、vivado工程详解PL端FPGA硬件设计…

多线程---线程池

文章目录 什么是线程池&#xff1f;线程池的实现标准库中的线程池&#xff08;四种&#xff09;自己实现一个线程池 线程池支持的参数在实际的开发中&#xff0c;线程池的线程数如何确定&#xff1f; 什么是线程池&#xff1f; 线程诞生的原因就是进程太“重量”了。虽然线程的…

【洛谷算法题】P5709-Apples Prologue / 苹果和虫子【入门2分支结构】

&#x1f468;‍&#x1f4bb;博客主页&#xff1a;花无缺 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 花无缺 原创 收录于专栏 【洛谷算法题】 文章目录 【洛谷算法题】P5709-Apples Prologue / 苹果和虫子【入门2分支结构】&#x1f30f;题目描述&am…

leetcode-哈希表

1. 理论 从哈希表的概念、哈希碰撞、哈希表的三种实现方式进行学习 哈希表&#xff1a;用来快速判断一个元素是否出现集合里。也就是查值就能快速判断&#xff0c;O&#xff08;1&#xff09;复杂度&#xff1b; 哈希碰撞&#xff1a;拉链法&#xff0c;线性探测法等。只是一种…

进程(详解)

进程 进程PCB进程的定义进程的组成进程模式进程的状态进程的运行进程的创建进程的结束孤儿进程僵尸进程僵尸进程的危害 进程的创建pidforkwait案例 进程 PCB 从操作系统理解进程概念-------先描述&#xff0c;后组织 为了使参与并发执行的程序能独立的运行&#xff0c;必须为之…

H5游戏源码分享-接苹果游戏拼手速

H5游戏源码分享-接苹果游戏拼手速 看看在20秒内能接多少个苹果 <html> <head><title>我是你的小苹果</title><meta charset"utf-8"/><meta name"viewport" content"initial-scale1, user-scalableno, minimum-scale…

【ARM 嵌入式 C 入门及渐进 10 -- 冒泡排序 选择排序 插入排序 快速排序 归并排序 堆排序 比较介绍】

文章目录 排序算法小结排序算法C实现 排序算法小结 C语言中常用的排序算法包括冒泡排序、选择排序、插入排序、快速排序、归并排序、堆排序。下面我们来一一介绍&#xff1a; 冒泡排序&#xff08;Bubble Sort&#xff09;&#xff1a;冒泡排序是通过比较相邻元素的大小进行排…

本机spark 通idea连接Oracle的坑

1. 报错&#xff1a;Exception in thread "main" java.lang.NoSuchMethodError: scala.Product.$init$(Lscala/Product;)V 查询网上资料&#xff0c;是idea引入的scala运行环境版本与idea默认的scala版本不一样 也就是写的项目中的pom的spark版本与idea默认的版本不…

【设计模式】第4节:创建型模式之“单例模式”

一、介绍 采取一定的方法保证在整个的软件系统中&#xff0c;对某个类只能存在一个对象实例&#xff0c;并且该类只提供一个取得其对象实例的方法。 不使用单例模式的UML类图&#xff1a; 使用单例模式的UML类图&#xff1a; 使用场景&#xff1a; 需要频繁创建或销毁的对象…

H5游戏分享-烟花效果

<!DOCTYPE html> <html dir"ltr" lang"zh-CN"> <head> <meta charset"UTF-8" /> <meta name"viewport" content"widthdevice-width" /> <title>点击夜空欣赏烟花</title> <sc…

Zabbix监控oxidized备份状态

Zabbix监控oxidized备份状态 原理是利用oxidized的hooks功能调用zabbix_sender推送数据给zabbix_server 参考 https://cloud.tencent.com/developer/article/1657025 https://github.com/clontarfx/zabbix-template-oxidized https://github.com/ytti/oxidized/blob/master/…

Python 日期和时间处理教程:datetime 模块的使用

Python 中的日期不是独立的数据类型&#xff0c;但我们可以导入一个名为 datetime 的模块来使用日期作为日期对象。 示例&#xff1a;导入 datetime 模块并显示当前日期&#xff1a; import datetimex datetime.datetime.now() print(x)日期输出 当我们执行上面示例中的代码…

如何确保PCIe Gen3通道的信号质量

PCIe 3.0设计面对的挑战 PCIe由PCI-SIG协会研发和维护的一个高速标准接口&#xff0c;PCIe3.0是其开发的第三代接口高速差分接口&#xff0c;其单个差分对信号速率可到达8.0Gbps&#xff0c;目前其以广泛的应用于计算机服务器等设备领域。 下图显示的是一个典型的PCIe Gen3的…

天气数据可视化平台-计算机毕业设计vue

天气变幻无常&#xff0c;影响着我们生活的方方面面&#xff0c;应用天气预报信息可以及时了解天气的趋势&#xff0c;给人们的工作、生活等带来便利&#xff0c;也可以为我们为未来的事情做安排和打算&#xff0c;所以一个精准的、易读 通过利用 程序对气象网站大量的气象信息…

ArcGIS笔记13_利用ArcGIS制作岸线与水深地形数据?建立水动力模型之前的数据收集与处理?

本文目录 前言Step 1 岸线数据Step 2 水深地形数据Step 3 其他数据及资料 前言 在利用MIKE建立水动力模型&#xff08;详见【MIKE水动力笔记】系列&#xff09;之前&#xff0c;需要收集、处理和制作诸多数据和资料&#xff0c;主要有岸线数据、水深地形数据、开边界潮位驱动数…

【C++】STL容器——探究不同 [ 迭代器 ] 种类&在STL中的使用方式(15)

前言 大家好吖&#xff0c;欢迎来到 YY 滴C系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; 目录 引言&#xff1a;一.查看STL使用文档时…