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

 某信用卡业务系统,银行账户存在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);
    }

    @Override
    public void deposit(double money) {
        userAccount.setBalance(userAccount.getBalance() + money);
    }

    @Override
    public void withdraw(double money) {
        userAccount.setBalance(userAccount.getBalance() - money);
    }

    @Override
    public void payInterest() {

    }

    @Override
    public String getState() {
        return "正常";
    }

}

public class OverdraftCardState extends CardState{

    public OverdraftCardState(UserAccount userAccount) {
        super(userAccount);
    }

    @Override
    public void deposit(double money) {
        payInterest();
        userAccount.setBalance(userAccount.getBalance() + money);
    }

    @Override
    public void withdraw(double money) {
        payInterest();
        userAccount.setBalance(userAccount.getBalance() - money);
    }

    @Override
    public void payInterest() {
        System.out.println("支付利息:" + userAccount.getBalance() * 0.01);
        userAccount.setBalance(userAccount.getBalance() * ( 1 + 0.01));
    }

    @Override
    public String getState() {
        return "透支";
    }
}

public class LimitationCardState extends CardState{

    public LimitationCardState(UserAccount userAccount) {
        super(userAccount);
    }

    @Override
    public void deposit(double money) {
        payInterest();
        userAccount.setBalance(userAccount.getBalance() + money);
    }

    @Override
    public void withdraw(double money) {
        System.out.println("该账户已受限,不能取款");
    }

    @Override
    public void payInterest() {
        System.out.println("支付利息:" + userAccount.getBalance() * 0.01);
        userAccount.setBalance(userAccount.getBalance() * ( 1 + 0.01));
    }

    @Override
    public 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{

    @Override
    public void on(LightSwitch lightSwitch) {
        System.out.println("灯泡已打开");
        System.out.println("--------------");
    }

    @Override
    public void off(LightSwitch lightSwitch) {
        System.out.println("关闭成功");
        LightSwitch.changeLightState("off");
        System.out.println("--------------");
    }

}

public class OffLightState extends LightState{

    @Override
    public void on(LightSwitch lightSwitch) {
        System.out.println("打开成功");
        LightSwitch.changeLightState("on");
        System.out.println("--------------");
    }

    @Override
    public 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)代码中包含大量与对象状态有关的条件语句。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【HarmonyOS】鸿蒙操作系统架构

HarmonyOS架构 一. 鸿蒙系统定位二. 架构整体遵从分层设计三. HarmonyOS具有的技术特性四. HarmonyOS有三大特征 其它相关推荐&#xff1a; 软考系统架构之案例篇(架构设计相关概念) 系统架构之微服务架构 系统架构设计之微内核架构 所属专栏&#xff1a;系统架构设计师 一. 鸿…

软考系列(系统架构师)- 2013年系统架构师软考案例分析考点

试题一 软件架构&#xff08;根据描述填表、ESB 定义和功能&#xff09; 【问题1】&#xff08;10分&#xff09; 服务建模是对Ramp Coordination信息系统进行集成的首要工作&#xff0c;公司的架构师首先对Ramp Coordination信息系统进行服务建模&#xff0c;识别出系统中的两…

window11 更改 vscode 插件目录,释放C盘内存

由于经常使用vscode开发会安装一些代码提示插件&#xff0c;然后C盘内容会逐渐缩小&#xff0c;最终排查定位到vscode。这个吃内存不眨眼的家伙。 建议&#xff1a;不要把插件目录和vscode安装目录放在同一个位置&#xff0c;不然这样vscode更新后&#xff0c;插件也会消失。 v…

OpenCV官方教程中文版 —— 2D 直方图

OpenCV官方教程中文版 —— 2D 直方图 前言一、介绍二、OpenCV 中的 2D 直方图三、Numpy 中 2D 直方图四、绘制 2D 直方图 前言 本节我们会学习如何绘制 2D 直方图&#xff0c;我们会在下一节中使用到它。 一、介绍 在前面的部分我们介绍了如何绘制一维直方图&#xff0c;之…

轻量级 IDE 文本编辑器 Geany 发布 2.0

Geany 是功能强大、稳定、轻量的开发者专用文本编辑器&#xff0c;支持 Linux、Windows 和 macOS&#xff0c;内置支持 50 多种编程语言。 2005 年Geany 发布首个版本 0.1。上周四刚好是 Geany 诞生 18 周年纪念日&#xff0c;官方发布了 2.0 正式版以表庆祝。 下载地址&#…

【机器学习可解释性】2.特征重要性排列

机器学习可解释性 1.模型洞察的价值2.特征重要性排列3.部分依赖图4.SHAP 值5.SHAP值的高级使用 正文 前言 你的模型认为哪些特征最重要&#xff1f; 介绍 我们可能会对模型提出的最基本的问题之一是&#xff1a;哪些特征对预测的影响最大&#xff1f; 这个概念被称为特征重…

antd+全屏的坑(全屏下a-modal/下拉框等不展示)

问题&#xff1a;针对某个元素全屏时&#xff0c;下拉框/弹窗/二次确认窗不展示&#xff1a;下拉框是往body里面插入的&#xff0c;全屏的元素盖住了下拉框。解决&#xff1a;给有下拉框的加入:append-to-body"false"&#xff08;缺点&#xff1a;每个都需要加&#…

Ansible中常用模块

1.ansible实现管理的方式 Ad-Hoc //利用ansible命令直接完成管理&#xff0c;主要用于临时命令使用场景 playbook //ansible脚本&#xff0c;主要用于大型项目场景&#xff0c;需要前期的规划 2.Ad-Hoc执行方式中如何获得帮助 ansible-doc …

测试C#调用Aplayer播放视频(1:加载Aplayer控件)

微信公众号“Dotnet跨平台”的文章《开源精品&#xff0c;使用 C# 开发的 KTV 点歌项目》中使用了迅雷开源APlayer播放引擎。最近在学习有哪些能拿来播放视频的组件或控件&#xff0c;于是准备试试&#xff0c;根据文章中的介绍&#xff0c;在迅雷APlayer播放引擎网站中下载了A…

Jetpack:023-Jetpack中的事件二

文章目录 1. 知识回顾2. 使用方法2.1 单击事件2.2 双击事件2.3 长按事件2.4 滑动事件 3. 示例代码4. 内容总结 我们在上一章回中介绍了 Jetpack中事件相关的内容&#xff0c;本章回中继续介绍这方面的内容。闲话休提&#xff0c;让我们一起Talk Android Jetpack吧&#xff01;…

消息中间件——RabbitMQ(一)Windows/Linux环境搭建(完整版)

前言 最近在学习消息中间件——RabbitMQ&#xff0c;打算把这个学习过程记录下来。此章主要介绍环境搭建。此次主要是单机搭建&#xff08;条件有限&#xff09;&#xff0c;包括在Windows、Linux环境下的搭建&#xff0c;以及RabbitMQ的监控平台搭建。 环境准备 在搭建Rabb…

matlab simulink 遗传算法优化RBF

1、内容简介 略 2-可以交流、咨询、答疑 2、内容说明 先用遗传算法优化RBF的权重系数&#xff0c;然后用RBF来做个控制器&#xff0c;查看效果 遗传算法、RBF控制、优化 3、仿真分析 4、参考论文 略

【AI视野·今日Robot 机器人论文速览 第六十期】Mon, 23 Oct 2023

AI视野今日CS.Robotics 机器人学论文速览 Mon, 23 Oct 2023 Totally 26 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Robotics Papers A Review of Prospects and Opportunities in Disassembly with Human-Robot Collaboration Authors Meng Lun Lee, Xiao Lian…

Kibana无法启动 kibana_task_manager search_phase_execution_exception 问题解决

今天服务器重启以后&#xff0c;发现无法启动Kibana服务了&#xff0c;在网上也是把错误搜索了半天&#xff0c;尝试了那些命令&#xff0c;但是执行删除命令时居然报错了。后来&#xff0c;仔细观察后&#xff0c;发现是删除方式不太正确&#xff0c;需要改一下删除命令才能正…

黑豹程序员-架构师学习路线图-百科:PowerDesigner数据库建模的行业标准

PowerDesigner最初由Xiao-Yun Wang&#xff08;王晓昀&#xff09;在SDP Technologies公司开发完成。 目前PowerDesigner是Sybase的企业建模和设计解决方案&#xff0c;采用模型驱动方法&#xff0c;将业务与IT结合起来&#xff0c;可帮助部署有效的企业体系架构&#xff0c;并…

【2023.10.30练习】C语言-循环右移字符

计算机能力挑战初赛2020.19题 题目描述&#xff1a; 现要对一个由字符a-z和A-Z组成的字符串进行解密&#xff0c;已知加密规则是&#xff1a; 字符串中所有字符分别在大写或小写的字母表中被循环左移5位(fGh-->aBc)&#xff0c; 输入&#xff1a;一个加密过的字符串&#…

强化学习------DDQN算法

前言 DQN算法 DQN算法有一个显著的问题&#xff0c;就是DQN估计的Q值往往会偏大。这是由于我们Q值是以下一个s’的Q值的最大值来估算的&#xff0c;但下一个state的Q值也是一个估算值&#xff0c;也依赖它的下一个state的Q值…&#xff0c;这就导致了Q值往往会有偏大的的情况…

设计模式之备忘录模式

文章目录 一、介绍二、应用举例三、基本角色四、代码演示五、总结 一、介绍 备忘录模式(Memento Pattern)&#xff0c;属于行为型设计模式。目的是用于保存一个对象在某一时刻的状态&#xff0c;以便于在将来某个时刻根据此状态恢复该对象。 在我们日常生活中&#xff0c;备忘…

【腾讯云 HAI域探秘】——锋体验官测试感受

腾讯云 HAR域探秘活动CSDN主页&#xff1a;腾讯云“HAI域探秘“新品先锋体验官招募&#xff0c;丰厚大奖限时领取&#xff01;_CSDN资讯的博客-CSDN博客 前言 参加这次活动感受到自动的好处&#xff0c;有句话叫做前人种树后人乘凉&#xff0c;对于我这样的AI小白来说&#x…

Android多张图片rotation旋转角度叠加/重叠堆放

Android多张图片rotation旋转角度叠加/重叠堆放 <?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:app"http://schemas.android.com/apk/res-auto"…